{"_id":"@airpinpoint/sdk","name":"@airpinpoint/sdk","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@airpinpoint/sdk","version":"1.0.0","description":"Official TypeScript SDK for the AirPinpoint asset tracking API","publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/bhaktatejas922/airtag-api.git","directory":"sdks/typescript"},"type":"module","sideEffects":false,"engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"},"./webhooks":{"types":"./dist/webhooks.d.ts","import":"./dist/webhooks.mjs","require":"./dist/webhooks.cjs"},"./utils":{"types":"./dist/utils/index.d.ts","import":"./dist/utils/index.mjs","require":"./dist/utils/index.cjs"}},"main":"./dist/index.cjs","module":"./dist/index.mjs","types":"./dist/index.d.ts","scripts":{"build":"tsup","typecheck":"tsc --noEmit"},"devDependencies":{"@types/node":"^25.6.0","tsup":"^8.0.0","typescript":"^5.4.0"},"keywords":["airpinpoint","airtag","asset-tracking","location","geofence","find-my"],"license":"MIT","gitHead":"984542a3969d5d137179ee1f5bebe1cf6ccf5629","_id":"@airpinpoint/sdk@1.0.0","bugs":{"url":"https://github.com/bhaktatejas922/airtag-api/issues"},"homepage":"https://github.com/bhaktatejas922/airtag-api#readme","_nodeVersion":"25.8.1","_npmVersion":"11.11.0","dist":{"integrity":"sha512-ioGywC9EN5lxNBOJ/bItJVGJk187JZNgOjAZzvFvjX8DPiAvQlduR90pkx6edwEUwCzZRF2/Ly8SDTnqW9B5vw==","shasum":"c8076876dfc36b7d2f91c51080da8ef8bf8468a6","tarball":"https://registry.npmjs.org/@airpinpoint/sdk/-/sdk-1.0.0.tgz","fileCount":22,"unpackedSize":107595,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDZQvwV1w0lcJvoGavOQ7oQ2yAezyDWitf+WMDHf1q95QIgcJE0L7QoB6VKGp/BvYDAzzpblYTv8IyupH32m4MdNK4="}]},"_npmUser":{"name":"airpinpoint-dev","email":"tejas@airpinpoint.com"},"directories":{},"maintainers":[{"name":"airpinpoint-dev","email":"tejas@airpinpoint.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sdk_1.0.0_1775897631785_0.5325085621554349"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-11T08:53:51.694Z","1.0.0":"2026-04-11T08:53:51.923Z","modified":"2026-04-11T08:53:52.119Z"},"maintainers":[{"name":"airpinpoint-dev","email":"tejas@airpinpoint.com"}],"description":"Official TypeScript SDK for the AirPinpoint asset tracking API","homepage":"https://github.com/bhaktatejas922/airtag-api#readme","keywords":["airpinpoint","airtag","asset-tracking","location","geofence","find-my"],"repository":{"type":"git","url":"git+https://github.com/bhaktatejas922/airtag-api.git","directory":"sdks/typescript"},"bugs":{"url":"https://github.com/bhaktatejas922/airtag-api/issues"},"license":"MIT","readme":"# AirPinpoint TypeScript SDK\n\nOfficial TypeScript/JavaScript SDK for the [AirPinpoint](https://airpinpoint.com) asset tracking API. Track AirTags and Find My compatible devices, manage geofences, receive webhook alerts, and analyze location data.\n\n- Zero runtime dependencies\n- ESM + CJS dual build\n- Node 18+, Deno, Bun, edge runtimes\n- Full TypeScript types\n- Auto-pagination\n- Retry with exponential backoff\n- Webhook signature verification\n\n## Installation\n\n```bash\nnpm install airpinpoint\n# or\nbun add airpinpoint\n# or\npnpm add airpinpoint\n```\n\n## Quick Start\n\n```typescript\nimport { AirPinpoint } from \"airpinpoint\";\n\nconst ap = new AirPinpoint({ apiKey: \"your-api-key\" });\n// Or set AIRPINPOINT_API_KEY env var and call new AirPinpoint()\n\n// Get account info\nconst account = await ap.account.retrieve();\n\n// List all trackables\nconst { data: trackables } = await ap.trackables.list();\n\n// Get current location of a specific device\nconst location = await ap.trackables.currentLocation(\"beacon-id\");\nconsole.log(`${location.latitude}, ${location.longitude}`);\n```\n\n## Trackables\n\n```typescript\n// List trackables (first page)\nconst { data } = await ap.trackables.list({ limit: 50 });\n\n// Auto-paginate through all trackables\nfor await (const trackable of ap.trackables.list()) {\n  console.log(trackable.name, trackable.enabled);\n}\n\n// Get a single trackable with last known location\nconst device = await ap.trackables.retrieve(\"beacon-id\");\nconsole.log(device.lastKnownLocation?.latitude);\n\n// Get current location\nconst loc = await ap.trackables.currentLocation(\"beacon-id\");\n\n// Get location history with time range\nconst history = await ap.trackables.locationHistory(\"beacon-id\", {\n  startTime: \"2026-01-01T00:00:00Z\",\n  endTime: \"2026-04-01T00:00:00Z\",\n  limit: 1000,\n});\n\n// Battery info\nconst battery = await ap.trackables.battery(\"beacon-id\");\nconsole.log(`${battery.estimatedDaysRemaining} days remaining`);\n\n// Reset battery tracking after replacement\nawait ap.trackables.resetBattery(\"beacon-id\", { batteryMonths: 12 });\n```\n\n## Geofences\n\n```typescript\n// Create a geofence\nconst fence = await ap.geofences.create({\n  name: \"Warehouse\",\n  latitude: 37.7749,\n  longitude: -122.4194,\n  radius: 500,\n  trackableId: [\"beacon-1\", \"beacon-2\"],\n  webhookEnabled: true,\n});\n\n// List geofences\nconst { data: fences } = await ap.geofences.list();\n\n// Filter by trackable\nconst { data: filtered } = await ap.geofences.list({\n  trackableId: \"beacon-1\",\n});\n\n// Update a geofence\nawait ap.geofences.update(fence.id, { radius: 1000 });\n\n// Delete a geofence\nawait ap.geofences.del(fence.id);\n\n// Test webhook delivery\nconst result = await ap.geofences.testWebhook(fence.id, {\n  eventType: \"entry\",\n});\n\n// List webhook deliveries\nconst deliveries = await ap.geofences.webhooks.list(fence.id);\n\n// Get delivery details\nconst detail = await ap.geofences.webhooks.retrieve(fence.id, deliveries[0].id);\n```\n\n## Share Links\n\n```typescript\nconst { shareUrl } = await ap.shareLinks.create({\n  trackableId: \"beacon-id\",\n  hours: 24,\n});\nconsole.log(shareUrl);\n```\n\n## Usage\n\n```typescript\nconst stats = await ap.usage.retrieve({\n  startDate: \"2026-01-01\",\n  endDate: \"2026-04-01\",\n});\n\nconst total = await ap.usage.total();\n```\n\n## Auto-Pagination\n\nEvery list method returns a `PaginatedList` that works both as a promise (first page) and as an async iterator (all pages):\n\n```typescript\n// First page only\nconst { data } = await ap.trackables.list({ limit: 10 });\n\n// Iterate all pages automatically\nfor await (const trackable of ap.trackables.list({ limit: 50 })) {\n  process.stdout.write(\".\");\n}\n```\n\n## Webhook Verification\n\nVerify incoming webhook signatures using the Web Crypto API (works in Node, Deno, Bun, and edge runtimes):\n\n```typescript\nimport { webhooks } from \"airpinpoint/webhooks\";\n\n// In your webhook handler\nconst event = await webhooks.constructEvent(\n  rawBody,                         // raw request body string\n  request.headers[\"x-airpinpoint-signature\"],\n  \"your-webhook-secret\",\n);\n\nswitch (event.event) {\n  case \"geofence.entry\":\n    console.log(`${event.beacon.name} entered ${event.geofence.name}`);\n    break;\n  case \"geofence.exit\":\n    console.log(`${event.beacon.name} exited ${event.geofence.name}`);\n    break;\n}\n```\n\n## Utility Functions\n\nLocation-specific utilities for analysis, filtering, and format conversion. Zero dependencies, pure functions.\n\n```typescript\nimport {\n  distance,\n  bearing,\n  isInsideGeofence,\n  detectStops,\n  detectTrips,\n  simplifyPath,\n  toGeoJSON,\n  encodePolyline,\n  filterByAccuracy,\n  filterBySpeed,\n  deduplicate,\n  batteryHealthStatus,\n} from \"airpinpoint/utils\";\n```\n\n### Geo\n\n```typescript\n// Distance between two points (meters)\nconst meters = distance(\n  { latitude: 37.7749, longitude: -122.4194 },\n  { latitude: 34.0522, longitude: -118.2437 },\n);\n\n// Bearing (degrees)\nconst deg = bearing(pointA, pointB);\n\n// Point-in-geofence check\nconst inside = isInsideGeofence(\n  { latitude: 37.775, longitude: -122.419 },\n  geofence, // any object with { latitude, longitude, radius }\n);\n```\n\n### Analysis\n\n```typescript\n// Detect stops (dwell clusters)\nconst stops = detectStops(locations, {\n  radiusM: 50,\n  minDurationMs: 300_000, // 5 minutes\n});\n\n// Detect trips between stops\nconst trips = detectTrips(locations);\n\n// Time spent inside a geofence\nconst ms = dwellTime(locations, geofence);\n\n// Simplify path (Douglas-Peucker)\nconst simplified = simplifyPath(locations, 10); // 10m tolerance\n```\n\n### Filtering\n\n```typescript\n// Remove inaccurate readings\nconst accurate = filterByAccuracy(locations, 50); // max 50m accuracy\n\n// Remove GPS teleport jumps\nconst clean = filterBySpeed(locations, 100); // max 100 m/s\n\n// Remove stale readings\nconst fresh = filterStale(locations, 3600_000); // max 1 hour old\n\n// Remove near-duplicates\nconst unique = deduplicate(locations, { radiusM: 5, timeWindowMs: 60_000 });\n```\n\n### Format Conversion\n\n```typescript\n// GeoJSON\nconst collection = toGeoJSON(locations);     // FeatureCollection<Point>\nconst line = toGeoJSONLineString(locations);  // Feature<LineString>\n\n// Google Encoded Polyline\nconst encoded = encodePolyline(locations);\nconst decoded = decodePolyline(encoded);\n```\n\n### Battery\n\n```typescript\nconst status = batteryHealthStatus(device.batteryInfo);\n// \"good\" | \"low\" | \"critical\" | \"replace\"\n```\n\n## Error Handling\n\n```typescript\nimport {\n  APIError,\n  AuthenticationError,\n  RateLimitError,\n  NotFoundError,\n  ValidationError,\n  APIConnectionError,\n} from \"airpinpoint\";\n\ntry {\n  await ap.trackables.retrieve(\"nonexistent\");\n} catch (err) {\n  if (err instanceof NotFoundError) {\n    console.log(\"Device not found\");\n  } else if (err instanceof RateLimitError) {\n    console.log(\"Rate limited, retry later\");\n  } else if (err instanceof AuthenticationError) {\n    console.log(\"Invalid API key\");\n  } else if (err instanceof APIConnectionError) {\n    console.log(\"Network error:\", err.message);\n  }\n}\n```\n\n## Configuration\n\n```typescript\nconst ap = new AirPinpoint({\n  apiKey: \"...\",                          // or AIRPINPOINT_API_KEY env var\n  baseURL: \"https://api.airpinpoint.com/v1\", // default\n  timeout: 30_000,                       // 30s default\n  maxRetries: 2,                         // retries on 429/5xx\n  fetch: customFetch,                    // custom fetch implementation\n});\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-032e67d594d989d91b030f6e086e8a8a"}