> For the complete documentation index, see [llms.txt](https://docs.jgopay.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.jgopay.com/english/authentication.md).

# Authentication

All API requests use a two-step authentication process: exchange your merchant credentials for a short-lived access token, then use that token for API calls.

## Step 1: Get Access Token

```http
POST /auth/token
Authorization: Basic base64(merchant_id:api_key)
Content-Type: application/json
```

### Example

```bash
# Encode credentials: base64("MRC_MYR_001:your_api_key")
curl -X POST https://api.pays3bucket.com/api/v1/auth/token \
  -H "Authorization: Basic TVJDX01ZUl8wMDE6eW91cl9hcGlfa2V5" \
  -H "Content-Type: application/json"
```

### Response

```json
{
  "status": "success",
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "token_type": "Bearer",
    "expires_in": 3600
  }
}
```

| Field          | Type   | Description                      |
| -------------- | ------ | -------------------------------- |
| `access_token` | string | JWT token for API calls          |
| `token_type`   | string | Always `Bearer`                  |
| `expires_in`   | number | Token lifetime in seconds (3600) |

## Step 2: Use Token

Include the token in the `Authorization` header for all subsequent requests:

```http
Authorization: Bearer <access_token>
Content-Type: application/json
```

## Important Notes

* Access tokens expire after **1 hour** (3600 seconds)
* Request a new token before the current one expires
* Never expose your `merchant_id` or `api_key` in client-side code
* Each token exchange creates a new session

## API Key Management

### Rotation

To rotate your API key:

1. Contact your account manager to request a key rotation
2. A new API key is generated — the **old key is immediately invalidated**
3. All active access tokens issued with the old key remain valid until they expire (up to 1 hour)
4. Update your integration with the new API key before the last active token expires

> **Important:** Have the new key ready to deploy before requesting rotation. There is no grace period — the old key stops working immediately.

### When to Rotate

| Trigger                     | Action                                  |
| --------------------------- | --------------------------------------- |
| Suspected key compromise    | Rotate immediately + review access logs |
| Team member departure       | Rotate within 24 hours                  |
| Key exposed in logs or code | Rotate immediately                      |
| Periodic maintenance        | Rotate every 90 days (recommended)      |

### Emergency Revocation

If your API key is compromised:

1. Contact your account manager immediately for emergency revocation
2. The key is deactivated — all new token exchange requests are rejected
3. Existing tokens continue working until they expire (max 1 hour)
4. A new key is issued and must be deployed to resume service

### Key Storage

* Store API keys in **environment variables** or a **secrets manager** (e.g., AWS Secrets Manager, HashiCorp Vault)
* **Never** hardcode keys in source code
* **Never** commit keys to version control (even in private repositories)
* **Never** include keys in client-side bundles, mobile apps, or browser code
* Restrict access to the key to only the services that need it

## Token Storage

### Server-Side Integrations (Recommended)

Store the access token in **memory** (a process variable). Tokens are short-lived (1 hour) and cheap to re-request, so persistence is unnecessary.

```javascript
let accessToken = null;
let tokenExpiresAt = 0;

async function getToken() {
  if (accessToken && Date.now() < tokenExpiresAt - 60000) {
    return accessToken; // Reuse if >1 minute before expiry
  }
  const response = await exchangeCredentials();
  accessToken = response.data.access_token;
  tokenExpiresAt = Date.now() + response.data.expires_in * 1000;
  return accessToken;
}
```

### Browser-Based Integrations

If your architecture requires browser-to-API communication:

* **Never** store tokens in `localStorage` or `sessionStorage` — these are accessible to any JavaScript on the page, including XSS-injected scripts
* Use **HttpOnly**, **Secure**, **SameSite=Strict** cookies set by your backend server
* Your backend acts as a proxy: browser talks to your server, your server talks to JGoPay
* If you must hold a token in browser memory, never persist it and clear it on page unload

### What NOT to Do

| Practice                       | Risk                                            |
| ------------------------------ | ----------------------------------------------- |
| Store token in `localStorage`  | XSS attack reads token, creates payments        |
| Embed API key in frontend code | Anyone can extract and use your credentials     |
| Log tokens in application logs | Log access = credential access                  |
| Share tokens between services  | Blast radius expands if one service is breached |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.jgopay.com/english/authentication.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
