Skip to content

IZI SDK Examples: JavaScript and Python

Published: · IZI Team

There’s no official IZI SDK — the API is standard GraphQL and works with any HTTP client. Here are production-ready wrapper classes for Node.js and Python with authentication, automatic token refresh, and error handling built in.

Окно терминала
npm install graphql-request graphql
izi-client.js
const { GraphQLClient, gql } = require('graphql-request');
const API_URL = 'https://api.izi.is/graphql';
class IZIClient {
constructor() {
this.client = new GraphQLClient(API_URL);
this.accessToken = null;
this.refreshToken = null;
this.tokenExpiresAt = null;
}
async login(email, password) {
const mutation = gql`
mutation Login($email: String!, $password: String!) {
loginWithEmailPassword(email: $email, password: $password) {
accessToken
refreshToken
}
}
`;
const data = await this.client.request(mutation, { email, password });
this._setTokens(data.loginWithEmailPassword);
return data.loginWithEmailPassword;
}
async _refreshTokens() {
const mutation = gql`
mutation Refresh($token: String!) {
refreshToken(token: $token) {
accessToken
refreshToken
}
}
`;
const data = await this.client.request(mutation, {
token: this.refreshToken,
});
this._setTokens(data.refreshToken);
}
_setTokens({ accessToken, refreshToken }) {
this.accessToken = accessToken;
this.refreshToken = refreshToken;
// accessToken lives 15 min, refresh 2 min before expiry
this.tokenExpiresAt = Date.now() + 13 * 60 * 1000;
this.client.setHeader('Authorization', `Bearer ${accessToken}`);
}
async request(query, variables = {}) {
if (this.tokenExpiresAt && Date.now() > this.tokenExpiresAt) {
await this._refreshTokens();
}
try {
return await this.client.request(query, variables);
} catch (error) {
const code = error.response?.errors?.[0]?.extensions?.code;
if (code === 'TOKEN_EXPIRED') {
await this._refreshTokens();
return await this.client.request(query, variables);
}
throw error;
}
}
}
module.exports = IZIClient;
const IZIClient = require('./izi-client');
const { gql } = require('graphql-request');
async function main() {
const izi = new IZIClient();
await izi.login(
process.env.IZI_EMAIL,
process.env.IZI_PASSWORD
);
// Get list of clubs
const { clubs } = await izi.request(gql`
query GetClubs {
clubs {
id
name
timezone
}
}
`);
console.log('Clubs:', clubs);
// Get active sessions
const { activeSessions } = await izi.request(
gql`
query ActiveSessions($clubId: ID!) {
activeSessions(clubId: $clubId) {
id
deviceName
clientName
startedAt
balance
}
}
`,
{ clubId: clubs[0].id }
);
console.log('Active sessions:', activeSessions.length);
}
main().catch(console.error);
webhook-handler.js
const express = require('express');
const crypto = require('crypto');
const app = express();
const WEBHOOK_SECRET = process.env.IZI_WEBHOOK_SECRET;
app.post(
'/izi/events',
express.raw({ type: 'application/json' }),
(req, res) => {
const signature = req.headers['x-izi-signature'];
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(req.body);
console.log(`Event: ${event.type}`, event.data);
switch (event.type) {
case 'SESSION_ENDED':
handleSessionEnded(event.data);
break;
case 'BALANCE_TOPPED_UP':
handleTopUp(event.data);
break;
}
res.status(200).send('OK');
}
);
app.listen(3000, () => console.log('Webhook server on :3000'));
apollo-client.js
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
const httpLink = createHttpLink({
uri: 'https://api.izi.is/graphql',
});
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem('izi_access_token');
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
};
});
export const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
});

Окно терминала
pip install gql[httpx] httpx
izi_client.py
import time
import hmac
import hashlib
from gql import gql, Client
from gql.transport.httpx import HTTPXTransport
API_URL = "https://api.izi.is/graphql"
class IZIClient:
def __init__(self):
self.access_token = None
self.refresh_token = None
self.token_expires_at = 0
self._transport = None
self._client = None
def _build_client(self):
headers = {}
if self.access_token:
headers["Authorization"] = f"Bearer {self.access_token}"
self._transport = HTTPXTransport(url=API_URL, headers=headers)
self._client = Client(transport=self._transport, fetch_schema_from_transport=False)
def login(self, email: str, password: str) -> dict:
self._build_client()
mutation = gql("""
mutation Login($email: String!, $password: String!) {
loginWithEmailPassword(email: $email, password: $password) {
accessToken
refreshToken
}
}
""")
result = self._client.execute(mutation, {"email": email, "password": password})
tokens = result["loginWithEmailPassword"]
self._set_tokens(tokens)
return tokens
def _refresh_tokens(self):
self._build_client()
mutation = gql("""
mutation Refresh($token: String!) {
refreshToken(token: $token) {
accessToken
refreshToken
}
}
""")
result = self._client.execute(mutation, {"token": self.refresh_token})
self._set_tokens(result["refreshToken"])
def _set_tokens(self, tokens: dict):
self.access_token = tokens["accessToken"]
self.refresh_token = tokens["refreshToken"]
# Refresh 2 minutes before expiry (TTL 15 min)
self.token_expires_at = time.time() + 13 * 60
self._build_client()
def request(self, query_str: str, variables: dict = None) -> dict:
if self.token_expires_at and time.time() > self.token_expires_at:
self._refresh_tokens()
query = gql(query_str)
return self._client.execute(query, variables or {})
main.py
import os
from izi_client import IZIClient
def main():
izi = IZIClient()
izi.login(
email=os.environ["IZI_EMAIL"],
password=os.environ["IZI_PASSWORD"]
)
result = izi.request("""
query GetClubs {
clubs {
id
name
timezone
}
}
""")
clubs = result["clubs"]
print(f"Clubs: {len(clubs)}")
sessions = izi.request("""
query ActiveSessions($clubId: ID!) {
activeSessions(clubId: $clubId) {
id
deviceName
clientName
startedAt
}
}
""", {"clubId": clubs[0]["id"]})
print(f"Active sessions: {len(sessions['activeSessions'])}")
if __name__ == "__main__":
main()
webhook_server.py
import os
import hmac
import hashlib
from flask import Flask, request, abort
app = Flask(__name__)
WEBHOOK_SECRET = os.environ["IZI_WEBHOOK_SECRET"]
def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
@app.post("/izi/events")
def handle_event():
signature = request.headers.get("X-IZI-Signature", "")
if not verify_signature(request.data, signature, WEBHOOK_SECRET):
abort(401)
event = request.get_json(force=True)
print(f"Event: {event['type']}", event.get("data"))
match event["type"]:
case "SESSION_ENDED":
handle_session_ended(event["data"])
case "BALANCE_TOPPED_UP":
handle_top_up(event["data"])
return "OK", 200

For both examples, use a .env file:

Окно терминала
IZI_EMAIL=partner@example.com
IZI_PASSWORD=your_secure_password
IZI_WEBHOOK_SECRET=your_webhook_secret

Never store tokens or passwords in the repository code.

Frequently asked questions

Is there an official IZI SDK?

There is no official package. IZI API is standard GraphQL and works with any GraphQL library: graphql-request, Apollo Client, gql + httpx. The examples below cover the main scenarios.

What dependencies are needed for Node.js integration?

Minimal: graphql-request. For production, add node-cron (token rotation) and pino or winston for logging.

How do I pass a token to Apollo Client?

Use authLink from @apollo/client/link/context. Example in the Apollo Client section below.