The LISA REST API uses a JWT token-based authentication system designed for third-party integrations and applications.
The authentication system consists of:
The API issues two kinds of access token. They authenticate differently, are scoped differently, and do not grant the same endpoints — picking the wrong one is the most common cause of an unexpected 403.
| Integrator token | User token | |
|---|---|---|
| Obtained with | POST /tokens with your integratorUID | POST /tokens/login with email + password |
| Identifies | a third-party integration (your organisation) | one BioCV user account |
| Can read | every account that has linked your integrator | only its own account |
| API tier | from the integrator's account (free / starter / pro / enterprise) | none — a fixed allowance of 1,000 calls per endpoint per day |
Raw sensor data (/sensordata*) | yes, on the Pro or Enterprise tier | no — at any tier |
If you need raw sensor data, you need an integrator token. A user token cannot reach /sensordata* no matter how its account is configured, so a 403 there is not fixed by an API tier change — it is fixed by authenticating as an integrator. If your organisation does not have an integrator account, contact BioCV.
To read a customer's data with an integrator token, that account must have linked your integrator first. The account owner authorises the link themselves via POST /partners/link using their own user token, or BioCV provisions it. An integrator cannot link itself to an account.
sequenceDiagram
participant Client
participant API
participant Database
Client->>API: POST /tokens (integratorUID)
API->>Database: Validate integratorUID
Database-->>API: Integrator data
API-->>Client: JWT tokens (access + refresh)
Client->>API: API Request (Authorization: Bearer token)
API->>API: Validate JWT token
API-->>Client: Response data
Generate JWT tokens using the token endpoint:
curl -X POST "https://api.biocv.info/api/v1/tokens" \
-H "Content-Type: application/json" \
-d '{
"integratorUID": "your-integrator-uid",
"expiresIn": 3600,
"refreshToken": true
}'
Request Body:
{
"integratorUID": "your-integrator-uid",
"expiresIn": 3600,
"refreshToken": true
}
Response:
{
"success": true,
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresAt": 1640995200000,
"tokenType": "Bearer"
},
"message": "Token generated successfully",
"timestamp": "2024-12-01T08:47:16.838Z"
}
Include the access token in the Authorization header:
curl -H "Authorization: Bearer your-access-token" \
"https://api.biocv.info/api/v1/users/user123/animals"
POST /tokens
Generate new JWT access and refresh tokens.
Parameters:
integratorUID (required) - Your integrator UIDexpiresIn (optional) - Token expiration in seconds (300-86400, default: 3600)refreshToken (optional) - Whether to include refresh token (default: false)POST /tokens/refresh
Refresh an expired access token using a refresh token.
curl -X POST "https://api.biocv.info/api/v1/tokens/refresh" \
-H "Content-Type: application/json" \
-d '{"refreshToken": "your-refresh-token"}'
Request Body:
{
"refreshToken": "your-refresh-token"
}
Response:
{
"success": true,
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresAt": 1640995200000,
"tokenType": "Bearer"
},
"message": "Token refreshed successfully",
"timestamp": "2024-12-01T08:47:16.838Z"
}
GET /tokens/validate
Validate a JWT token and get token information.
curl -H "Authorization: Bearer your-access-token" \
"https://api.biocv.info/api/v1/tokens/validate"
Response:
{
"success": true,
"data": {
"valid": true,
"integratorUID": "your-integrator-uid",
"integrator": {
"email": "integrator@example.com",
"name": "Integrator Name",
"isActive": true
},
"tokenType": "access",
"expiresAt": "2024-12-01T09:47:16.838Z"
},
"message": "Token is valid",
"timestamp": "2024-12-01T08:47:16.838Z"
}
POST /tokens/revoke
Revoke a JWT token (logout).
curl -X POST "https://api.biocv.info/api/v1/tokens/revoke" \
-H "Authorization: Bearer your-access-token"
Response:
{
"success": true,
"message": "Token revoked successfully",
"timestamp": "2024-12-01T08:47:16.838Z"
}
GET /tokens/info
Get detailed token information (requires valid token).
curl -H "Authorization: Bearer your-access-token" \
"https://api.biocv.info/api/v1/tokens/info"
Response:
{
"success": true,
"data": {
"integratorUID": "your-integrator-uid",
"integrator": {
"email": "integrator@example.com",
"name": "Integrator Name",
"isActive": true,
"createdAt": "2024-01-01T00:00:00.000Z"
},
"token": {
"type": "access",
"issuedAt": "2024-12-01T08:47:16.838Z",
"expiresAt": "2024-12-01T09:47:16.838Z",
"jti": "token-unique-id"
},
"config": {
"accessTokenExpiry": 3600,
"refreshTokenExpiry": 604800
}
},
"message": "Token information retrieved successfully",
"timestamp": "2024-12-01T08:47:16.838Z"
}
Implement automatic token refresh in your application:
class LISAPIClient {
constructor(integratorUID) {
this.integratorUID = integratorUID;
this.accessToken = null;
this.refreshToken = null;
}
async ensureValidToken() {
if (!this.accessToken || this.isTokenExpired()) {
if (this.refreshToken && !this.isRefreshTokenExpired()) {
await this.refreshAccessToken();
} else {
await this.generateNewTokens();
}
}
}
isTokenExpired() {
if (!this.accessToken) return true;
const payload = JSON.parse(atob(this.accessToken.split('.')[1]));
return Date.now() >= payload.exp * 1000;
}
async refreshAccessToken() {
const response = await fetch('https://api.biocv.info/api/v1/tokens/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken: this.refreshToken })
});
const data = await response.json();
if (data.success) {
this.accessToken = data.data.accessToken;
this.refreshToken = data.data.refreshToken;
}
}
}
Missing Token:
{
"success": false,
"error": "Authorization header is required",
"timestamp": "2024-12-01T08:47:16.838Z"
}
Invalid Token:
{
"success": false,
"error": "Invalid token",
"timestamp": "2024-12-01T08:47:16.838Z"
}
Expired Token:
{
"success": false,
"error": "Token has expired",
"timestamp": "2024-12-01T08:47:16.838Z"
}
Invalid Integrator:
{
"success": false,
"error": "Access denied: Invalid integratorUID",
"timestamp": "2024-12-01T08:47:16.838Z"
}
Token endpoints have stricter rate limits:
In addition to the IP limit, authenticated requests are subject to a per-endpoint,
per-day account quota. For integrator tokens this follows the integrator's API tier
(Free 10, Starter 1,000, Pro 10,000, Enterprise unlimited). User tokens have a fixed
allowance of 1,000 calls per endpoint per day, which no tier setting changes. Both
reads and writes count, and the quota resets at 00:00 UTC. Monitor the
X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-Reset headers.
See Endpoints and Error Handling for details.
class LISAPIClient {
constructor(integratorUID, baseURL = 'https://api.biocv.info/api/v1') {
this.integratorUID = integratorUID;
this.baseURL = baseURL;
this.accessToken = null;
this.refreshToken = null;
}
async generateTokens(expiresIn = 3600) {
const response = await fetch(`${this.baseURL}/tokens`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
integratorUID: this.integratorUID,
expiresIn,
refreshToken: true
})
});
const data = await response.json();
if (data.success) {
this.accessToken = data.data.accessToken;
this.refreshToken = data.data.refreshToken;
return data.data;
}
throw new Error(data.error);
}
async makeRequest(endpoint, options = {}) {
await this.ensureValidToken();
const url = `${this.baseURL}${endpoint}`;
const headers = {
'Authorization': `Bearer ${this.accessToken}`,
'Content-Type': 'application/json',
...options.headers
};
const response = await fetch(url, { ...options, headers });
const data = await response.json();
if (!data.success) {
throw new Error(`API Error: ${data.error}`);
}
return data;
}
}
import requests
import time
class LISAPIClient:
def __init__(self, integrator_uid, base_url="https://api.biocv.info/api/v1"):
self.integrator_uid = integrator_uid
self.base_url = base_url
self.access_token = None
self.refresh_token = None
self.token_expires_at = None
def generate_tokens(self, expires_in=3600):
response = requests.post(f"{self.base_url}/tokens",
json={
"integratorUID": self.integrator_uid,
"expiresIn": expires_in,
"refreshToken": True
}
)
data = response.json()
if data["success"]:
self.access_token = data["data"]["accessToken"]
self.refresh_token = data["data"]["refreshToken"]
self.token_expires_at = data["data"]["expiresAt"]
return data["data"]
raise Exception(f"Failed to generate tokens: {data.get('error', 'Unknown error')}")
def is_token_expired(self):
if not self.token_expires_at:
return True
return time.time() * 1000 >= self.token_expires_at
def ensure_valid_token(self):
if not self.access_token or self.is_token_expired():
if self.refresh_token and not self.is_token_expired():
self.refresh_access_token()
else:
self.generate_tokens()
def make_request(self, method, endpoint, **kwargs):
self.ensure_valid_token()
headers = {
"Authorization": f"Bearer {self.access_token}",
"Content-Type": "application/json"
}
url = f"{self.base_url}{endpoint}"
response = requests.request(method, url, headers=headers, **kwargs)
data = response.json()
if not data.get("success", False):
raise Exception(f"API Error: {data.get('error', 'Unknown error')}")
return data
/sensordata* with a user token: expected — raw sensor data is not available to user tokens at any tier. Use an integrator token (POST /tokens) on the Pro or Enterprise tier. An API tier change will not help.POST /partners/link, or BioCV provisions it./tokens/validateNext: API Endpoints - Complete endpoint documentation
Diese Seite wird aus dem offiziellen LiSA-API-Wiki erzeugt. Sollte etwas falsch sein oder fehlen, kontaktieren Sie uns — wir korrigieren es an der Quelle.
Wiki-Stand e01e79e · 19.8.2026