mapflow-co-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,671 @@
1
+ # MapFlow Node.js SDK
2
+
3
+ **Official Node.js & TypeScript SDK for [MapFlow](https://mapflow.co) — Route Optimization & Delivery Management API**
4
+
5
+ [![npm version](https://img.shields.io/npm/v/mapflow-co-sdk.svg)](https://www.npmjs.com/package/mapflow-co-sdk)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.2-blue.svg)](https://www.typescriptlang.org/)
7
+ [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D16-green.svg)](https://nodejs.org/)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+ [![API Docs](https://img.shields.io/badge/API-Docs-blue.svg)](https://mapflow.readme.io/reference)
10
+
11
+ MapFlow is a SaaS platform for **route optimization**, **delivery planning**, and **logistics management**. This SDK gives JavaScript and TypeScript developers full programmatic access to the MapFlow API — manage customers, warehouses, drivers, vehicles, delivery schedules, and hierarchical product structures from your own applications.
12
+
13
+ → **Website**: https://mapflow.co
14
+ → **API Documentation**: https://mapflow.readme.io/reference
15
+ → **Get your API key**: app.mapflow.co → Settings → API Keys
16
+ → **Python SDK**: https://github.com/mapflow-co/python-sdk
17
+
18
+ ---
19
+
20
+ ## Table of Contents
21
+
22
+ - [Features](#features)
23
+ - [Requirements](#requirements)
24
+ - [Installation](#installation)
25
+ - [Quick Start](#quick-start)
26
+ - [Authentication](#authentication)
27
+ - [Core Resources](#core-resources)
28
+ - [Customers](#customers)
29
+ - [Delivery Locations](#delivery-locations)
30
+ - [Warehouses](#warehouses)
31
+ - [Drivers & Pickers](#drivers--pickers)
32
+ - [Vehicles](#vehicles)
33
+ - [Product Catalog](#product-catalog)
34
+ - [Visits & Scheduling](#visits--scheduling)
35
+ - [Visit Products](#visit-products)
36
+ - [Contacts](#contacts)
37
+ - [Opening Hours](#opening-hours)
38
+ - [Tags](#tags)
39
+ - [Global Customer](#global-customer-atomic-creation)
40
+ - [Pagination](#pagination)
41
+ - [Bulk Operations](#bulk-operations)
42
+ - [Error Handling](#error-handling)
43
+ - [TypeScript Support](#typescript-support)
44
+ - [Examples](#examples)
45
+ - [Support](#support)
46
+
47
+ ---
48
+
49
+ ## Features
50
+
51
+ - **Full API coverage** — customers, locations, warehouses, drivers, vehicles, catalog, visits, tags, contacts, and opening hours
52
+ - **TypeScript first** — complete type definitions with full IntelliSense and autocompletion
53
+ - **Promise-based** — modern async/await throughout
54
+ - **Typed errors** — `AuthenticationError`, `NotFoundError`, `ValidationError`, `RateLimitError`, and more
55
+ - **Paginated responses** — generic `PaginatedResponse<T>` with automatic deserialization
56
+ - **Bulk operations** — activate, deactivate, update, tag multiple records in one request
57
+ - **Flexible input** — pass typed objects or plain records to any write method
58
+ - **Zero bloat** — only `axios` as a runtime dependency
59
+ - **Node.js ≥ 16** compatible
60
+
61
+ ---
62
+
63
+ ## Requirements
64
+
65
+ - Node.js ≥ 16.0.0
66
+ - `axios` ≥ 1.6.0 (installed automatically)
67
+
68
+ ---
69
+
70
+ ## Installation
71
+
72
+ ```bash
73
+ npm install mapflow-co-sdk
74
+ ```
75
+
76
+ ```bash
77
+ yarn add mapflow-co-sdk
78
+ ```
79
+
80
+ ```bash
81
+ pnpm add mapflow-co-sdk
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Quick Start
87
+
88
+ ```typescript
89
+ import { MapFlowClient, CustomerType, VisitType } from 'mapflow-co-sdk';
90
+
91
+ const client = new MapFlowClient({ apiKey: 'your-api-key' });
92
+
93
+ // Create a customer
94
+ const customer = await client.createCustomer({
95
+ customer_type: CustomerType.COMPANY,
96
+ company_name: 'Acme Corp',
97
+ email: 'contact@acme.com',
98
+ billing_address: '42 Rue de la Paix',
99
+ billing_zip_code: '75001',
100
+ billing_city: 'Paris',
101
+ billing_country: 'FR',
102
+ });
103
+
104
+ // Create a delivery location
105
+ const location = await client.createDeliveryLocation({
106
+ customer: customer.id,
107
+ name: 'Main Office',
108
+ address: '42 Rue de la Paix',
109
+ zip_code: '75001',
110
+ city: 'Paris',
111
+ country: 'FR',
112
+ latitude: 48.8566,
113
+ longitude: 2.3522,
114
+ });
115
+
116
+ // Schedule a delivery visit
117
+ const visit = await client.createVisit({
118
+ location: location.id,
119
+ visit_type: VisitType.DELIVERY,
120
+ visit_date: '2026-04-01',
121
+ planned_arrival_time: new Date('2026-04-01T09:00:00').toISOString(),
122
+ notes: 'Ring bell at entrance',
123
+ });
124
+
125
+ console.log(`Visit scheduled: ${visit.id}`);
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Authentication
131
+
132
+ All requests require an API key sent as the `X-API-Key` header. Get your key from [app.mapflow.co](https://app.mapflow.co) → Settings → API Keys.
133
+
134
+ ```typescript
135
+ const client = new MapFlowClient({
136
+ apiKey: 'your-api-key', // required
137
+ baseUrl: 'https://api.mapflow.co', // optional — default shown
138
+ timeout: 30000, // optional — milliseconds
139
+ });
140
+ ```
141
+
142
+ Use environment variables for security:
143
+
144
+ ```typescript
145
+ const client = new MapFlowClient({
146
+ apiKey: process.env.MAPFLOW_API_KEY!,
147
+ baseUrl: process.env.MAPFLOW_BASE_URL,
148
+ });
149
+ ```
150
+
151
+ ```bash
152
+ # .env
153
+ MAPFLOW_API_KEY=your-api-key-here
154
+ ```
155
+
156
+ ---
157
+
158
+ ## Core Resources
159
+
160
+ ### Customers
161
+
162
+ Manage individual and business customers including billing details, VAT numbers, and SIRET.
163
+
164
+ ```typescript
165
+ import { MapFlowClient, CustomerType } from 'mapflow-co-sdk';
166
+
167
+ const client = new MapFlowClient({ apiKey: 'your-api-key' });
168
+
169
+ // Create
170
+ const customer = await client.createCustomer({
171
+ customer_type: CustomerType.COMPANY,
172
+ company_name: 'Acme Corporation',
173
+ email: 'contact@acme.com',
174
+ phone: '+33123456789',
175
+ billing_address: '123 Rue de la Paix',
176
+ billing_zip_code: '75001',
177
+ billing_city: 'Paris',
178
+ billing_country: 'FR',
179
+ });
180
+
181
+ // List with filters
182
+ const customers = await client.listCustomers({
183
+ is_active: true,
184
+ customer_type: 'company',
185
+ });
186
+ for (const c of customers.results) {
187
+ console.log(c.display_name, c.email);
188
+ }
189
+
190
+ // Read / Update / Delete
191
+ const found = await client.getCustomer(customer.id);
192
+ await client.patchCustomer(customer.id, { notes: 'VIP client' });
193
+ await client.deleteCustomer(customer.id);
194
+ ```
195
+
196
+ ### Delivery Locations
197
+
198
+ Physical addresses where deliveries or pickups take place, with geolocation and access constraints.
199
+
200
+ ```typescript
201
+ const location = await client.createDeliveryLocation({
202
+ customer: customer.id,
203
+ name: 'Main Warehouse',
204
+ address: '456 Avenue des Champs',
205
+ zip_code: '75008',
206
+ city: 'Paris',
207
+ country: 'FR',
208
+ latitude: 48.8566,
209
+ longitude: 2.3522,
210
+ delivery_instructions: 'Loading dock on the left',
211
+ access_code: '1234',
212
+ });
213
+
214
+ const locations = await client.listDeliveryLocations({ city: 'Paris' });
215
+ ```
216
+
217
+ ### Warehouses
218
+
219
+ Operational bases for your fleet — supports start/end points, loading docks, and multi-vehicle assignment.
220
+
221
+ ```typescript
222
+ import { WarehouseType } from 'mapflow-co-sdk';
223
+
224
+ const warehouse = await client.createWarehouse({
225
+ name: 'Paris Nord Hub',
226
+ code: 'PARIS-01',
227
+ warehouse_type: WarehouseType.HUB,
228
+ address: '12 Rue Industrielle',
229
+ zip_code: '93200',
230
+ city: 'Saint-Denis',
231
+ latitude: 48.9356,
232
+ longitude: 2.3539,
233
+ opening_time: '08:00:00',
234
+ closing_time: '18:00:00',
235
+ is_start_point: true,
236
+ is_end_point: true,
237
+ max_vehicles: 50,
238
+ });
239
+
240
+ await client.setDefaultWarehouse(warehouse.id);
241
+ ```
242
+
243
+ ### Drivers & Pickers
244
+
245
+ Manage drivers and warehouse order pickers with licence types, certifications, and vehicle capabilities.
246
+
247
+ ```typescript
248
+ import { UserRole, DriverLicenceType, VehicleType, Language } from 'mapflow-co-sdk';
249
+
250
+ const driver = await client.createDriver({
251
+ email: 'driver@example.com',
252
+ first_name: 'Jean',
253
+ last_name: 'Dupont',
254
+ phone: '+33612345678',
255
+ role: UserRole.DRIVER,
256
+ language: Language.FR,
257
+ has_valid_driving_license: true,
258
+ driver_licence_type: [DriverLicenceType.B, DriverLicenceType.C],
259
+ vehicle_types: [VehicleType.VAN_MEDIUM],
260
+ });
261
+
262
+ await client.patchDriver(driver.id, { employee_id: 'EMP-001' });
263
+ ```
264
+
265
+ ### Vehicles
266
+
267
+ Fleet management including capacity, fuel type, maintenance status, and GPS tracking.
268
+
269
+ ```typescript
270
+ import { VehicleType, EnergyType } from 'mapflow-co-sdk';
271
+
272
+ const vehicle = await client.createVehicle({
273
+ name: 'Van 01',
274
+ license_plate: 'AB-123-CD',
275
+ vehicle_type: VehicleType.VAN_MEDIUM,
276
+ brand: 'Renault',
277
+ model: 'Master',
278
+ year: 2023,
279
+ energy_type: EnergyType.DIESEL,
280
+ max_weight_kg: 1500,
281
+ max_volume_m3: 12.0,
282
+ assigned_warehouses: [warehouse.id],
283
+ });
284
+ ```
285
+
286
+ ### Product Catalog
287
+
288
+ Define products, services, packages, and pallets with weight, volume, pricing, and temperature constraints.
289
+
290
+ ```typescript
291
+ import { ItemType, WeightUnit, VolumeUnit } from 'mapflow-co-sdk';
292
+
293
+ const product = await client.createDeliveryItem({
294
+ name: 'Laptop Pro 16',
295
+ reference: 'PROD-001',
296
+ item_type: ItemType.PRODUCT,
297
+ weight: 2.1,
298
+ weight_unit: WeightUnit.KG,
299
+ length: 36,
300
+ width: 25,
301
+ height: 2,
302
+ selling_price: 2499.00,
303
+ currency: 'EUR',
304
+ is_fragile: true,
305
+ });
306
+
307
+ const fragile = await client.listDeliveryItems({
308
+ item_type: ItemType.PRODUCT,
309
+ is_fragile: true,
310
+ });
311
+ ```
312
+
313
+ ### Visits & Scheduling
314
+
315
+ Schedule delivery, pickup, or service stops at delivery locations with driver and vehicle assignment.
316
+
317
+ ```typescript
318
+ import { VisitType } from 'mapflow-co-sdk';
319
+
320
+ const visit = await client.createVisit({
321
+ location: location.id,
322
+ visit_type: VisitType.DELIVERY,
323
+ visit_date: '2026-04-01',
324
+ planned_arrival_time: '2026-04-01T09:00:00.000Z',
325
+ planned_departure_time: '2026-04-01T10:00:00.000Z',
326
+ driver: driver.id,
327
+ vehicle: vehicle.id,
328
+ priority: 3,
329
+ notes: 'Ring bell at entrance',
330
+ tags: [tagId],
331
+ products: [{ product: product.id, quantity: 3 }],
332
+ });
333
+
334
+ const visits = await client.listVisits({
335
+ visit_date: '2026-04-01',
336
+ visit_type: 'delivery',
337
+ });
338
+ ```
339
+
340
+ ### Visit Products
341
+
342
+ Link catalog items to a scheduled visit with quantities.
343
+
344
+ ```typescript
345
+ const visitProduct = await client.createVisitProduct({
346
+ visit: visit.id,
347
+ product: product.id,
348
+ quantity: 3,
349
+ });
350
+
351
+ await client.patchVisitProduct(visitProduct.id, { quantity: 5 });
352
+ ```
353
+
354
+ ### Contacts
355
+
356
+ Manage contacts linked to delivery locations.
357
+
358
+ ```typescript
359
+ const contact = await client.createContact({
360
+ first_name: 'Marie',
361
+ last_name: 'Martin',
362
+ position: 'Logistics Manager',
363
+ emails: ['marie@techsolutions.fr'],
364
+ phones: ['+33987654321'],
365
+ is_primary: true,
366
+ location_ids: [location.id],
367
+ });
368
+ ```
369
+
370
+ ### Opening Hours
371
+
372
+ Define opening and closing times per day of week for delivery locations.
373
+
374
+ ```typescript
375
+ import { DayOfWeek } from 'mapflow-co-sdk';
376
+
377
+ const hours = await client.createOpeningHours({
378
+ location: location.id,
379
+ day_of_week: DayOfWeek.MONDAY,
380
+ opening_time: '09:00:00',
381
+ closing_time: '18:00:00',
382
+ is_closed: false,
383
+ });
384
+ ```
385
+
386
+ ### Tags
387
+
388
+ Color-coded labels for visits, drivers, and customers.
389
+
390
+ ```typescript
391
+ const tag = await client.createTag({
392
+ name: 'Urgent',
393
+ color: '#FF0000',
394
+ description: 'Priority deliveries',
395
+ });
396
+
397
+ await client.customerBulkAction({
398
+ action: 'add_tags',
399
+ customer_ids: [c1.id, c2.id],
400
+ tag_ids: [tag.id],
401
+ });
402
+ ```
403
+
404
+ ### Global Customer (Atomic Creation)
405
+
406
+ Create a customer, delivery location, contact, and opening hours in a single atomic request.
407
+
408
+ ```typescript
409
+ import { CustomerType, DayOfWeek } from 'mapflow-co-sdk';
410
+
411
+ const globalCustomer = await client.createGlobalCustomer({
412
+ customer_type: CustomerType.COMPANY,
413
+ company_name: 'Tech Solutions SARL',
414
+ email: 'contact@techsolutions.fr',
415
+ delivery_location: {
416
+ name: 'Head Office',
417
+ address: '10 Rue de la Tech',
418
+ zip_code: '69001',
419
+ city: 'Lyon',
420
+ country: 'FR',
421
+ },
422
+ contact: {
423
+ first_name: 'Marie',
424
+ last_name: 'Martin',
425
+ position: 'Logistics Manager',
426
+ emails: ['marie@techsolutions.fr'],
427
+ is_primary: true,
428
+ },
429
+ opening_hours: [
430
+ { day_of_week: DayOfWeek.MONDAY, opening_time: '09:00:00', closing_time: '18:00:00' },
431
+ { day_of_week: DayOfWeek.TUESDAY, opening_time: '09:00:00', closing_time: '18:00:00' },
432
+ ],
433
+ });
434
+ ```
435
+
436
+ ---
437
+
438
+ ## Pagination
439
+
440
+ All list endpoints return a `PaginatedResponse<T>` with full IDE autocomplete on results.
441
+
442
+ ```typescript
443
+ const page = await client.listCustomers({ page: 1, page_size: 20 });
444
+
445
+ console.log(`Total: ${page.count}`);
446
+ console.log(`Next: ${page.next}`);
447
+
448
+ for (const customer of page.results) {
449
+ console.log(customer.display_name);
450
+ }
451
+
452
+ // Iterate all pages with an async generator
453
+ async function* allCustomers() {
454
+ let pageNum = 1;
455
+ while (true) {
456
+ const page = await client.listCustomers({ page: pageNum, page_size: 50 });
457
+ yield* page.results;
458
+ if (!page.next) break;
459
+ pageNum++;
460
+ }
461
+ }
462
+
463
+ for await (const customer of allCustomers()) {
464
+ console.log(customer.id);
465
+ }
466
+ ```
467
+
468
+ ---
469
+
470
+ ## Bulk Operations
471
+
472
+ Most resources support bulk actions to reduce API round-trips.
473
+
474
+ ```typescript
475
+ // Activate / deactivate customers
476
+ await client.customerBulkAction({ action: 'activate', customer_ids: [id1, id2, id3] });
477
+
478
+ // Change vehicle status
479
+ await client.vehicleBulkAction({ action: 'change_status', vehicle_ids: [v1, v2], new_status: 'maintenance' });
480
+
481
+ // Bulk tagging
482
+ await client.customerBulkAction({ action: 'add_tags', customer_ids: [id1, id2], tag_ids: [tag.id] });
483
+
484
+ // Bulk product quantity update
485
+ await client.visitProductBulkAction({ action: 'update_quantity', visitproduct_ids: [vp1, vp2], new_quantity: 5 });
486
+
487
+ // Bulk delivery item flags
488
+ await client.deliveryItemBulkAction({ action: 'update_fragile', delivery_item_ids: [p1, p2], is_fragile: true });
489
+ ```
490
+
491
+ ---
492
+
493
+ ## Error Handling
494
+
495
+ The SDK raises typed exceptions for every HTTP error class.
496
+
497
+ ```typescript
498
+ import {
499
+ MapFlowError,
500
+ AuthenticationError, // 401
501
+ ForbiddenError, // 403
502
+ NotFoundError, // 404
503
+ ValidationError, // 400
504
+ RateLimitError, // 429
505
+ ServerError, // 5xx
506
+ } from 'mapflow-co-sdk';
507
+
508
+ try {
509
+ const customer = await client.getCustomer(customerId);
510
+ } catch (error) {
511
+ if (error instanceof AuthenticationError) {
512
+ console.error('Invalid API key — check your credentials at app.mapflow.co → Settings → API Keys');
513
+ } else if (error instanceof NotFoundError) {
514
+ console.error('Customer not found');
515
+ } else if (error instanceof ValidationError) {
516
+ console.error('Validation error:', error.message);
517
+ console.error('Details:', error.response);
518
+ } else if (error instanceof RateLimitError) {
519
+ console.error('Rate limit exceeded — slow down requests');
520
+ } else if (error instanceof ServerError) {
521
+ console.error('MapFlow server error — try again later');
522
+ } else if (error instanceof MapFlowError) {
523
+ console.error(`Error ${error.statusCode}: ${error.message}`);
524
+ }
525
+ }
526
+ ```
527
+
528
+ ---
529
+
530
+ ## TypeScript Support
531
+
532
+ This SDK is written in TypeScript and ships complete type definitions.
533
+
534
+ ```typescript
535
+ import type {
536
+ Customer,
537
+ CustomerCreate,
538
+ DeliveryLocation,
539
+ Vehicle,
540
+ Visit,
541
+ PaginatedResponse,
542
+ } from 'mapflow-co-sdk';
543
+
544
+ const data: CustomerCreate = {
545
+ customer_type: CustomerType.COMPANY,
546
+ company_name: 'Example Corp',
547
+ billing_city: 'Paris',
548
+ billing_country: 'FR',
549
+ };
550
+
551
+ const page: PaginatedResponse<Customer> = await client.listCustomers();
552
+ ```
553
+
554
+ ### Enums Reference
555
+
556
+ | Enum | Values |
557
+ |------|--------|
558
+ | `CustomerType` | `individual`, `company` |
559
+ | `ItemType` | `PRODUCT`, `SERVICE`, `PACKAGE`, `PALLET` |
560
+ | `VisitType` | `delivery`, `pickup`, `service`, `delivery_pickup` |
561
+ | `VehicleType` | `bicycle`, `cargo_bike`, `motorcycle`, `van_small`, `van_medium`, `van_large`, `truck_small`, `truck_medium`, `truck_large`, `semi_trailer`, `refrigerated`, … |
562
+ | `VehicleStatus` | `available`, `in_use`, `maintenance`, `broken`, `retired` |
563
+ | `EnergyType` | `gasoline`, `diesel`, `electric`, `hybrid`, `hydrogen` |
564
+ | `DriverLicenceType` | `B`, `C`, `CE`, `C1`, `C1E`, `D`, `DE` |
565
+ | `WarehouseType` | `distribution`, `storage`, `hub`, `pickup`, `cross_dock`, `other` |
566
+ | `WeightUnit` | `kg`, `g`, `lb`, `oz`, `t` |
567
+ | `VolumeUnit` | `m3`, `l`, `ml`, `cm3`, `ft3`, `gal` |
568
+ | `DayOfWeek` | `MONDAY` (0) … `SUNDAY` (6) |
569
+ | `Language` | `fr`, `en`, `es`, `de`, `it` |
570
+ | `UserRole` | `admin`, `manager`, `operator`, `driver` |
571
+
572
+ ---
573
+
574
+ ## Examples
575
+
576
+ The `examples/` directory contains ready-to-run scripts:
577
+
578
+ ```bash
579
+ export MAPFLOW_API_KEY="your-api-key"
580
+ npx ts-node examples/basic-usage.ts
581
+ ```
582
+
583
+ ---
584
+
585
+ ## API Reference — Client Methods
586
+
587
+ ### Customers
588
+ `listCustomers(params?)` · `createCustomer(data)` · `getCustomer(id)` · `updateCustomer(id, data)` · `patchCustomer(id, data)` · `deleteCustomer(id)` · `customerBulkAction(action)`
589
+
590
+ ### Delivery Locations
591
+ `listDeliveryLocations(params?)` · `createDeliveryLocation(data)` · `getDeliveryLocation(id)` · `updateDeliveryLocation(id, data)` · `patchDeliveryLocation(id, data)` · `deleteDeliveryLocation(id)` · `deliveryLocationBulkAction(action)`
592
+
593
+ ### Warehouses
594
+ `listWarehouses(params?)` · `createWarehouse(data)` · `getWarehouse(id)` · `updateWarehouse(id, data)` · `patchWarehouse(id, data)` · `deleteWarehouse(id)` · `setDefaultWarehouse(id)` · `warehouseBulkAction(action)`
595
+
596
+ ### Drivers & Pickers
597
+ `listDrivers(params?)` · `createDriver(data)` · `getDriver(id)` · `updateDriver(id, data)` · `patchDriver(id, data)` · `deleteDriver(id)`
598
+
599
+ ### Vehicles
600
+ `listVehicles(params?)` · `createVehicle(data)` · `getVehicle(id)` · `updateVehicle(id, data)` · `patchVehicle(id, data)` · `deleteVehicle(id)` · `vehicleBulkAction(action)`
601
+
602
+ ### Product Catalog
603
+ `listDeliveryItems(params?)` · `createDeliveryItem(data)` · `getDeliveryItem(id)` · `updateDeliveryItem(id, data)` · `patchDeliveryItem(id, data)` · `deleteDeliveryItem(id)` · `deliveryItemBulkAction(action)`
604
+
605
+ ### Visits
606
+ `listVisits(params?)` · `createVisit(data)` · `getVisit(id)` · `updateVisit(id, data)` · `patchVisit(id, data)` · `deleteVisit(id)` · `visitBulkAction(action)`
607
+
608
+ ### Visit Products
609
+ `listVisitProducts(params?)` · `createVisitProduct(data)` · `getVisitProduct(id)` · `updateVisitProduct(id, data)` · `patchVisitProduct(id, data)` · `deleteVisitProduct(id)` · `visitProductBulkAction(action)`
610
+
611
+ ### Tags
612
+ `listTags(params?)` · `createTag(data)` · `getTag(id)` · `updateTag(id, data)` · `patchTag(id, data)` · `deleteTag(id)` · `tagBulkAction(action)`
613
+
614
+ ### Contacts
615
+ `listContacts(params?)` · `createContact(data)` · `getContact(id)` · `updateContact(id, data)` · `patchContact(id, data)` · `deleteContact(id)`
616
+
617
+ ### Opening Hours
618
+ `listOpeningHours(params?)` · `createOpeningHours(data)` · `getOpeningHours(id)` · `updateOpeningHours(id, data)` · `patchOpeningHours(id, data)` · `deleteOpeningHours(id)`
619
+
620
+ ### Global Customer
621
+ `createGlobalCustomer(data)` — atomic creation of customer + location + contact + hours
622
+
623
+ ---
624
+
625
+ ## Development
626
+
627
+ ```bash
628
+ # Install dependencies
629
+ npm install
630
+
631
+ # Build
632
+ npm run build
633
+
634
+ # Watch mode
635
+ npm run build:watch
636
+
637
+ # Run tests
638
+ npm test
639
+
640
+ # Lint & format
641
+ npm run lint
642
+ npm run format
643
+ ```
644
+
645
+ ---
646
+
647
+ ## Contributing
648
+
649
+ Contributions are welcome! Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our code of conduct and pull request process.
650
+
651
+ ---
652
+
653
+ ## Support
654
+
655
+ - **Website**: https://mapflow.co
656
+ - **API Documentation**: https://mapflow.readme.io/reference
657
+ - **Email**: support@mapflow.co
658
+ - **GitHub Issues**: https://github.com/mapflow-co/node-sdk/issues
659
+
660
+ ---
661
+
662
+ ## Related SDKs
663
+
664
+ - **Python SDK**: https://github.com/mapflow-co/python-sdk — `pip install mapflow-co-sdk`
665
+ - **Node.js SDK**: https://github.com/mapflow-co/node-sdk — `npm install mapflow-co-sdk`
666
+
667
+ ---
668
+
669
+ ## License
670
+
671
+ MIT © [MapFlow](https://mapflow.co)