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

# Authentication

> API authentication methods and examples

# API Authentication

All protected endpoints require authentication. This page covers the authentication methods available.

## Authentication Methods

| Method       | Header(s)                                                      | Use Case                                |
| ------------ | -------------------------------------------------------------- | --------------------------------------- |
| Bearer token | `Authorization: Bearer ...`                                    | Admin and standard protected API routes |
| POS HMAC     | `X-API-Key-ID`, `X-Timestamp`, `X-Signature`                   | POS terminal integrations               |
| Partner HMAC | `X-API-Key-ID`, `X-Partner-ID`, `X-Timestamp`, `X-Signature`   | Partner API routes                      |
| Service Auth | `X-Service-Name`, `X-Service-Timestamp`, `X-Service-Signature` | Internal services                       |

## Bearer Authentication

Used by admin users and other bearer-protected routes.

### Request Format

```bash theme={null}
curl -X GET https://demo.api.vultlocal.com/api/v1/admin/users \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```

### API Key Prefixes

| Prefix        | Environment     |
| ------------- | --------------- |
| `olive_live_` | Production      |
| `olive_test_` | Staging/Testing |

## POS HMAC Authentication

POS routes under `/api/v1/pos/*` are protected by HMAC, not bearer tokens.

Mintlify's manual endpoint playground cannot generate these HMAC headers automatically, so POS pages use `playground: "simple"` and provide copy-paste cURL examples.

### Request Format

```bash theme={null}
BODY='{"card_serial":"OLIV0001","pin":"1234"}'
TIMESTAMP='2026-03-10T12:00:00Z'
SIGNATURE=$(printf 'POST\n/api/v1/pos/verify-card\n%s\n%s' "$TIMESTAMP" "$BODY" | openssl dgst -sha256 -hmac "$OLIVE_HMAC_SECRET" -hex | sed 's/^.* //')

curl -X POST https://demo.api.vultlocal.com/api/v1/pos/verify-card \
  -H "X-API-Key-ID: $OLIVE_API_KEY_ID" \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "X-Signature: $SIGNATURE" \
  -H "Content-Type: application/json" \
  -d "$BODY"
```

The signature string is exactly:

```text theme={null}
POST
/api/v1/pos/verify-card
2026-03-10T12:00:00Z
{"card_serial":"OLIV0001","pin":"1234"}
```

<Info>
  The `/payment/*` routes use the same HMAC authentication but do **not** require a `pin` field in the request body. The partner's HMAC signature provides the security instead of the cardholder PIN.
</Info>

## JWT Authentication

For admin operations and protected dashboard endpoints.

### Obtaining a Token

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://demo.api.vultlocal.com/api/v1/admin/login \
    -H "Content-Type: application/json" \
    -d '{
          "email": "admin@olive.example.com",
          "password": "your-password"
    }'
  ```

  ```json Response theme={null}
  {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "expires_in": 86400
  }
  ```
</CodeGroup>

### Using the Token

```bash theme={null}
curl -X GET https://demo.api.vultlocal.com/api/v1/admin/users \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```

## Error Responses

### 401 Unauthorized

Missing or invalid authentication:

```json theme={null}
{
  "error": "Unauthorized",
  "code": "UNAUTHORIZED",
  "message": "Invalid or missing authentication token"
}
```

### 403 Forbidden

Authenticated but lacking permission:

```json theme={null}
{
  "error": "Forbidden",
  "code": "FORBIDDEN",
  "message": "Insufficient permissions for this operation"
}
```

## Service Authentication

Internal service routes use dedicated service headers, not `X-Service-Auth`.

### Headers

```http theme={null}
X-Service-Name: wallet-core
X-Service-Timestamp: 1710072000
X-Service-Signature: <hex-hmac>
```

### Signature Format

The canonical string is pipe-delimited:

```text theme={null}
METHOD|PATH|QUERY|TIMESTAMP
```

If there is no query string, keep the empty segment:

```text theme={null}
POST|/api/v1/compliance/alerts||1710072000
```

## HMAC Routes In Docs

* Partner and POS pages use `playground: "simple"` because their signatures must be computed from the exact raw request body.
* Bearer-protected and public routes can keep the interactive playground.

## Best Practices

<AccordionGroup>
  <Accordion title="Secure Storage">
    Store API keys securely; never expose in client-side code or version control.
  </Accordion>

  <Accordion title="Key Rotation">
    Rotate API keys periodically and immediately after any suspected compromise.
  </Accordion>

  <Accordion title="Minimum Scope">
    Request only the scopes needed for your integration.
  </Accordion>

  <Accordion title="HTTPS Only">
    Always use HTTPS in production to protect credentials in transit.
  </Accordion>
</AccordionGroup>
