Installation
Install the SDK using your preferred package manager:
npm install @demurly/sdkRequires Node.js 18+ or a modern Edge runtime.
Quick Start
Initialize the client with your API key and make your first request:
import { Demurly } from '@demurly/sdk';
// Initialize the client
const demurly = new Demurly({
apiKey: process.env.DEMURLY_API_KEY!,
});
// List all records
const records = await demurly.records.list({
status: 'pending_approval',
limit: 20,
});
console.log(`Found ${records.data.length} records`);Get your API key
Records API
List Records
Retrieve demurrage records with optional filters for status, date range, terminal, and more.
// List records with filters
const records = await demurly.records.list({
status: 'approved',
terminalId: 'trm_abc123',
startDate: '2024-01-01',
endDate: '2024-01-31',
page: 1,
limit: 50,
});
// Iterate through results
for (const record of records.data) {
console.log(`Record ${record.id}: $${record.demurrageAmount}`);
}Get Record
Fetch a single record with full details including GPS points, photos, and metadata.
// Get a single record with full details
const record = await demurly.records.get('rec_abc123');
console.log({
terminal: record.terminalName,
driver: record.driverName,
waitTime: record.waitTimeMinutes,
amount: record.demurrageAmount,
status: record.status,
});Create Record
Start a new demurrage record when a driver arrives at a terminal.
// Create a new demurrage record
const record = await demurly.records.create({
driverId: 'drv_456',
terminalId: 'trm_789',
arrivalTime: new Date().toISOString(),
latitude: 29.7604,
longitude: -95.3698,
});
console.log(`Created record: ${record.id}`);Approve & Dispute
Approve records for payment or dispute them with a reason.
// Approve a record (customer only)
await demurly.records.approve('rec_abc123');
// Dispute a record
await demurly.records.dispute('rec_abc123', {
reason: 'Incorrect arrival time',
details: 'Driver arrived at 8:45 AM, not 8:30 AM',
});Drivers API
Manage drivers in your organization — list, create, update, and deactivate.
// List all active drivers
const drivers = await demurly.drivers.list({
status: 'active',
});
// Get a specific driver
const driver = await demurly.drivers.get('drv_123');
// Create a new driver
const newDriver = await demurly.drivers.create({
firstName: 'John',
lastName: 'Smith',
phone: '+15551234567',
email: 'john.smith@example.com',
});
// Deactivate a driver
await demurly.drivers.deactivate('drv_123');Webhook Verification
Verify webhook signatures to ensure events are from Demurly. Always verify before processing!
import { Demurly } from '@demurly/sdk';
import express from 'express';
const app = express();
// Use raw body for webhook verification
app.post('/webhooks/demurly',
express.raw({ type: 'application/json' }),
(req, res) => {
const signature = req.headers['x-demurly-signature'] as string;
const payload = req.body;
try {
// Verify the webhook signature
const event = Demurly.webhooks.verify(
payload,
signature,
process.env.DEMURLY_WEBHOOK_SECRET!
);
// Handle the event
switch (event.type) {
case 'record.approved':
console.log('Record approved:', event.data.id);
break;
case 'payment.completed':
console.log('Payment received:', event.data.amount);
break;
default:
console.log('Unhandled event:', event.type);
}
res.json({ received: true });
} catch (err) {
console.error('Webhook verification failed:', err);
res.status(400).json({ error: 'Invalid signature' });
}
}
);Security
Error Handling
The SDK provides typed error classes for different failure scenarios:
import {
Demurly,
DemurlyError,
AuthenticationError,
RateLimitError,
NotFoundError,
ValidationError,
} from '@demurly/sdk';
const demurly = new Demurly({ apiKey: 'your_api_key' });
try {
const record = await demurly.records.get('rec_invalid');
} catch (error) {
if (error instanceof AuthenticationError) {
// Invalid or expired API key
console.error('Authentication failed. Check your API key.');
} else if (error instanceof RateLimitError) {
// Too many requests
console.error(`Rate limited. Retry after ${error.retryAfter}s`);
} else if (error instanceof NotFoundError) {
// Resource not found
console.error('Record not found');
} else if (error instanceof ValidationError) {
// Invalid request parameters
console.error('Validation errors:', error.errors);
} else if (error instanceof DemurlyError) {
// Generic Demurly error
console.error(`Error ${error.code}: ${error.message}`);
} else {
// Unknown error
throw error;
}
}AuthenticationErrorInvalid/expired API key
RateLimitErrorToo many requests
NotFoundErrorResource not found
ValidationErrorInvalid parameters
TypeScript Types
All types are exported for use in your application:
// All types are exported from the SDK
import type {
Record,
Driver,
Terminal,
Organization,
Payment,
WebhookEvent,
RecordStatus,
ListRecordsParams,
CreateRecordParams,
} from '@demurly/sdk';
// TypeScript will enforce correct types
const params: ListRecordsParams = {
status: 'approved', // Type-checked!
limit: 20,
};
const records: Record[] = await demurly.records.list(params);