JavaScript Examples
Using fetch (Node.js 18+ / Browser)
Helper
javascript
const API_BASE = 'https://api.looma.chat/v1';
const API_KEY = 'lm_your_key';
async function api(path, options = {}) {
const response = await fetch(`${API_BASE}${path}`, {
...options,
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
...options.headers,
},
});
if (!response.ok) {
const body = await response.json();
const err = new Error(body.error?.message || 'API error');
err.status = response.status;
err.code = body.error?.code;
throw err;
}
return response.json();
}List Leads
javascript
const { data, meta } = await api('/leads?status=new&per_page=50');
for (const lead of data) {
console.log(`${lead.id}: ${lead.status} — ${lead.lead_text}`);
}
console.log(`Page ${meta.page} of ${meta.total_pages}`);Get Lead Details
javascript
const { data: lead } = await api('/leads/456');
console.log(lead.collected_fields);Update Lead Status
javascript
await api('/leads/456', {
method: 'PATCH',
body: JSON.stringify({
status: 'in_progress',
assigned_operator_id: 5,
}),
});Add Comment
javascript
await api('/leads/456/comments', {
method: 'POST',
body: JSON.stringify({
text: 'Called the customer, waiting for callback.',
}),
});List Chats
javascript
const { data: chats } = await api('/chats?status=operator&per_page=20');
for (const chat of chats) {
console.log(`${chat.session_id}: ${chat.status}`);
}Send Chat Message
javascript
await api('/chats/session_abc123/messages', {
method: 'POST',
body: JSON.stringify({
text: 'Hello! How can I help you today?',
}),
});Upload File to Chat
javascript
const fs = require('fs');
const FormData = require('form-data');
const form = new FormData();
form.append('file', fs.createReadStream('/path/to/document.pdf'));
form.append('message', 'Please see the attached document');
const response = await fetch(
`${API_BASE}/chats/session_abc123/files`,
{
method: 'POST',
headers: { 'Authorization': `Bearer ${API_KEY}` },
body: form,
}
);
const result = await response.json();Get Balance
javascript
const { data: balance } = await api('/balance');
console.log(`Credits: ${balance.credits}`);
console.log(`USD: ${balance.usd_equivalent}`);Create Webhook
javascript
const { data: webhook } = await api('/webhooks', {
method: 'POST',
body: JSON.stringify({
url: 'https://your-app.com/webhooks/looma',
events: ['lead.created', 'chat.message.new'],
}),
});
// Save webhook.secret — shown only once!
console.log('Secret:', webhook.secret);Export Leads as CSV
javascript
const response = await fetch(
`${API_BASE}/leads/export?status=new&date_from=2026-01-01`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
const csv = await response.text();
require('fs').writeFileSync('leads.csv', csv);Pagination
javascript
async function getAllLeads(filters = {}) {
const params = new URLSearchParams({ per_page: '100', ...filters });
const leads = [];
let page = 1;
while (true) {
params.set('page', page);
const { data, meta } = await api(`/leads?${params}`);
leads.push(...data);
if (page >= meta.total_pages) break;
page++;
}
return leads;
}
const allNewLeads = await getAllLeads({ status: 'new' });Error Handling with Retry
javascript
async function apiWithRetry(path, options = {}, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await api(path, options);
} catch (err) {
if (err.status === 429 && attempt < maxRetries) {
const retryAfter = parseInt(err.retryAfter || '60', 10);
console.log(`Rate limited. Retrying in ${retryAfter}s...`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
continue;
}
throw err;
}
}
}Webhook Signature Verification (Express)
javascript
const crypto = require('crypto');
const express = require('express');
const app = express();
app.use('/webhooks/looma', express.raw({ type: 'application/json' }));
app.post('/webhooks/looma', (req, res) => {
const payload = req.body.toString();
const timestamp = req.headers['x-looma-timestamp'];
const signature = req.headers['x-looma-signature'];
// Verify signature
const expected = crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(timestamp + '.' + payload)
.digest('hex');
if (expected !== signature) {
return res.status(401).send('Invalid signature');
}
// Reject old requests (> 5 minutes)
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
return res.status(401).send('Timestamp too old');
}
const event = JSON.parse(payload);
switch (event.event) {
case 'lead.created':
console.log('New lead:', event.data);
break;
case 'chat.message.new':
console.log('New message:', event.data);
break;
}
res.status(200).send('OK');
});
app.listen(3000);
