diff --git a/README.md b/README.md index c4a8b18..c09fd04 100644 --- a/README.md +++ b/README.md @@ -26,64 +26,26 @@ const client = new AccessGrid(accountId, secretKey); #### Provision a new card ```javascript -// employee badge const card = await client.accessCards.provision({ cardTemplateId: "0xd3adb00b5", employeeId: "123456789", - cardNumber: "42069", - siteCode: "55", + tagId: "DDEADB33FB00B5", + allowOnMultipleDevices: true, fullName: "Employee name", email: "employee@yourwebsite.com", phoneNumber: "+19547212241", classification: "full_time", - startDate: "2025-01-31T22:46:25.601Z", - expirationDate: "2025-04-30T22:46:25.601Z", - employeePhoto: "[image_in_base64_encoded_format]" -}); - -// Card object contains details like: -console.log(card.id); // The card's unique ID -console.log(card.url); // Installation URL for the card -console.log(card.state); // Current state (active, suspended, etc.) -console.log(card.fullName); // Employee name - -// hotel -const card = await client.accessCards.provision({ - cardTemplateId: "0xd3adb00b5", - cardNumber: "1", - fileData: "0000000000000000000000000000000000000000000000000000000000000420", - fullName: "Employee name", - email: "employee@yourwebsite.com", - phoneNumber: "+19547212241", - memberId: "MEM123", - membershipStatus: "Guest", - isPassReadyToTransact: true, - tileData: { - checkInAvailableWindowStartDateTime: "2025-05-05T23:46:25.601Z", - checkInAvailableWindowEndDateTime: "2025-05-10T23:46:25.601Z", - checkInURL: "https://checkin.com", - isCheckedIn: false, - numberOfRoomsReserved: 1, - roomNumbers: ["101"] - }, - reservations: [{ - isCheckedIn: false, - numberOfRoomsReserved: 1, - roomNumbers: ["101"], - propertyLocation: "Calle Retama 22, Zaragoza, 50720, Spain", - propertyName: "Omnitec Hotel", - reservationStartDateTime: "2025-05-10T12:00:00.000Z", - reservationEndDateTime: "2025-05-12T12:00:00.000Z", - reservationNumber: "123" - }], - expirationDate: "2025-05-12T14:00:00.000Z" + startDate: new Date().toISOString(), + expirationDate: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString(), + employeePhoto: "[image_in_base64_encoded_format]", + title: "Engineering Manager", + metadata: { + department: "engineering", + badgeType: "contractor" + } }); -// Card object contains details like: -console.log(card.id); // The card's unique ID -console.log(card.url); // Installation URL for the card -console.log(card.state); // Current state (active, suspended, etc.) -console.log(card.fullName); // Employee name +console.log(`Install URL: ${card.url}`); ``` You can also use the `issue()` method as an alias for `provision()`. @@ -106,19 +68,18 @@ console.log('Devices:', card.devices); console.log('Metadata:', card.metadata); ``` -#### List cards for a template +#### List cards ```javascript -// Get all cards for a template -const cards = await client.accessCards.list("template-id"); +// Get filtered keys by template +const templateKeys = await client.accessCards.list({ templateId: "0xd3adb00b5" }); -// Filter by state -const activeCards = await client.accessCards.list("template-id", "active"); -// Possible states: "active", "suspended", "unlink", "deleted" +// Get filtered keys by state +const activeKeys = await client.accessCards.list({ state: "active" }); -// Access card properties -cards.forEach(card => { - console.log(`${card.fullName} (${card.id}): ${card.state}`); +// Print keys +templateKeys.forEach(key => { + console.log(`Key ID: ${key.id}, Name: ${key.fullName}, State: ${key.state}`); }); ``` @@ -131,7 +92,8 @@ const card = await client.accessCards.update({ fullName: "Updated Employee Name", classification: "contractor", expirationDate: "2025-02-22T21:04:03.664Z", - employeePhoto: "[image_in_base64_encoded_format]" + employeePhoto: "[image_in_base64_encoded_format]", + title: "Senior Developer" }); ``` @@ -139,24 +101,16 @@ const card = await client.accessCards.update({ ```javascript // Suspend a card -await client.accessCards.suspend({ - cardId: "0xc4rd1d" -}); +await client.accessCards.suspend({ cardId: "0xc4rd1d" }); // Resume a card -await client.accessCards.resume({ - cardId: "0xc4rd1d" -}); +await client.accessCards.resume({ cardId: "0xc4rd1d" }); // Unlink a card -await client.accessCards.unlink({ - cardId: "0xc4rd1d" -}); +await client.accessCards.unlink({ cardId: "0xc4rd1d" }); // Delete a card -await client.accessCards.delete({ - cardId: "0xc4rd1d" -}); +await client.accessCards.delete({ cardId: "0xc4rd1d" }); ``` ### Enterprise Console @@ -165,36 +119,28 @@ await client.accessCards.delete({ ```javascript const template = await client.console.createTemplate({ - name: "Employee NFC key", + name: "Employee Access Pass", platform: "apple", useCase: "employee_badge", protocol: "desfire", allowOnMultipleDevices: true, watchCount: 2, iphoneCount: 3, - design: { - backgroundColor: "#FFFFFF", - labelColor: "#000000", - labelSecondaryColor: "#333333", - backgroundImage: "[image_in_base64_encoded_format]", - logoImage: "[image_in_base64_encoded_format]", - iconImage: "[image_in_base64_encoded_format]" - }, - supportInfo: { - supportUrl: "https://help.yourcompany.com", - supportPhoneNumber: "+1-555-123-4567", - supportEmail: "support@yourcompany.com", - privacyPolicyUrl: "https://yourcompany.com/privacy", - termsAndConditionsUrl: "https://yourcompany.com/terms" + backgroundColor: "#FFFFFF", + labelColor: "#000000", + labelSecondaryColor: "#333333", + supportUrl: "https://help.yourcompany.com", + supportPhoneNumber: "+1-555-123-4567", + supportEmail: "support@yourcompany.com", + privacyPolicyUrl: "https://yourcompany.com/privacy", + termsAndConditionsUrl: "https://yourcompany.com/terms", + metadata: { + version: "2.1", + approvalStatus: "approved" } }); -// Template object contains details like: -console.log(template.id); // Template ID -console.log(template.name); // Template name -console.log(template.platform); // Platform (apple, etc.) -console.log(template.issuedKeysCount); // Number of keys issued -console.log(template.activeKeysCount); // Number of active keys +console.log(`Template created successfully: ${template.id}`); ``` #### Update a template @@ -202,16 +148,21 @@ console.log(template.activeKeysCount); // Number of active keys ```javascript const template = await client.console.updateTemplate({ cardTemplateId: "0xd3adb00b5", - name: "Updated Employee NFC key", + name: "Updated Employee Access Pass", allowOnMultipleDevices: true, watchCount: 2, iphoneCount: 3, - supportInfo: { - supportUrl: "https://help.yourcompany.com", - supportPhoneNumber: "+1-555-123-4567", - supportEmail: "support@yourcompany.com", - privacyPolicyUrl: "https://yourcompany.com/privacy", - termsAndConditionsUrl: "https://yourcompany.com/terms" + backgroundColor: "#FFFFFF", + labelColor: "#000000", + labelSecondaryColor: "#333333", + supportUrl: "https://help.yourcompany.com", + supportPhoneNumber: "+1-555-123-4567", + supportEmail: "support@yourcompany.com", + privacyPolicyUrl: "https://yourcompany.com/privacy", + termsAndConditionsUrl: "https://yourcompany.com/terms", + metadata: { + version: "2.2", + lastUpdatedBy: "admin" } }); ``` @@ -222,82 +173,207 @@ const template = await client.console.updateTemplate({ const template = await client.console.readTemplate({ cardTemplateId: "0xd3adb00b5" }); + +console.log(`Template ID: ${template.id}`); +console.log(`Name: ${template.name}`); +console.log(`Platform: ${template.platform}`); +console.log(`Protocol: ${template.protocol}`); +console.log(`Multi-device: ${template.allowOnMultipleDevices}`); ``` #### Get event logs ```javascript -const events = await client.console.getEventLogs({ +const events = await client.console.eventLog({ cardTemplateId: "0xd3adb00b5", filters: { - device: "mobile", // "mobile" or "watch" + device: "mobile", startDate: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(), endDate: new Date().toISOString(), - eventType: "install" // "install", "activate", etc. + eventType: "install" } }); -// You can also use the eventLog() method as an alias -const events = await client.console.eventLog({ - // Same parameters as above +events.forEach(event => { + console.log(`Event: ${event.type} at ${event.timestamp} by ${event.userId}`); }); ``` -## Configuration +#### List pass template pairs + +```javascript +const result = await client.console.listPassTemplatePairs({ + page: 1, + perPage: 50 +}); + +result.passTemplatePairs.forEach(pair => { + console.log(`Pair: ${pair.name} (ID: ${pair.id})`); + console.log(` Android: ${pair.androidTemplate?.name}`); + console.log(` iOS: ${pair.iosTemplate?.name}`); +}); +``` + +#### List ledger items + +```javascript +const result = await client.console.ledgerItems({ + page: 1, + perPage: 50, + startDate: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(), + endDate: new Date().toISOString() +}); + +result.ledgerItems.forEach(item => { + console.log(`Amount: ${item.amount}, Kind: ${item.kind}, Date: ${item.createdAt}`); + if (item.accessPass) { + console.log(` Access Pass: ${item.accessPass.exId}`); + if (item.accessPass.passTemplate) console.log(` Card Template: ${item.accessPass.passTemplate.exId}`); + } +}); + +console.log(`Page ${result.pagination.currentPage} of ${result.pagination.totalPages}`); +``` + +#### iOS preflight + +```javascript +const response = await client.console.iosPreflight({ + cardTemplateId: "0xt3mp14t3-3x1d", + accessPassExId: "0xp455-3x1d" +}); + +console.log(`Provisioning Credential ID: ${response.provisioningCredentialIdentifier}`); +console.log(`Sharing Instance ID: ${response.sharingInstanceIdentifier}`); +console.log(`Card Template ID: ${response.cardTemplateIdentifier}`); +console.log(`Environment ID: ${response.environmentIdentifier}`); +``` + +### HID Orgs + +#### Create a HID org + +```javascript +const org = await client.console.hid.orgs.create({ + name: 'My Org', + fullAddress: '1 Main St, NY NY', + phone: '+1-555-0000', + firstName: 'Ada', + lastName: 'Lovelace' +}); + +console.log(`Created org: ${org.name} (ID: ${org.id})`); +console.log(`Slug: ${org.slug}`); +``` + +#### List HID orgs + +```javascript +const orgs = await client.console.hid.orgs.list(); + +orgs.forEach(org => { + console.log(`Org ID: ${org.id}, Name: ${org.name}, Slug: ${org.slug}`); +}); +``` + +#### Activate a HID org + +```javascript +const result = await client.console.hid.orgs.activate({ + email: 'admin@example.com', + password: 'hid-password-123' +}); -The SDK can be configured with custom options: +console.log(`Completed registration for org: ${result.name}`); +console.log(`Status: ${result.status}`); +``` + +### Webhooks + +```javascript +const express = require('express'); +const app = express(); + +app.use(express.json({ + type: ['application/json', 'application/cloudevents+json'] +})); + +app.post('/webhooks', (req, res) => { + const payload = req.body; + + if (payload.specversion !== '1.0') { + return res.status(400).json({ error: 'Invalid CloudEvents format' }); + } + + switch (payload.type) { + case 'ag.access_pass.issued': + console.log(`Access pass issued: ${payload.data.access_pass_id}`); + break; + case 'ag.access_pass.activated': + console.log(`Access pass activated: ${payload.data.access_pass_id}`); + break; + case 'ag.card_template.published': + console.log(`Template published: ${payload.data.card_template_id}`); + break; + } + + res.status(200).json({ received: true }); +}); +``` + +## Configuration ```javascript const client = new AccessGrid(accountId, secretKey, { - baseUrl: 'https://api.staging.accessgrid.com' // Use a different API endpoint + baseUrl: 'https://api.staging.accessgrid.com' }); ``` ## Error Handling -The SDK provides specific error classes: - ```javascript import { AccessGridError, AuthenticationError } from 'accessgrid'; try { - const card = await client.accessCards.provision({ - // ... parameters - }); + const card = await client.accessCards.provision({ /* ... */ }); } catch (error) { if (error instanceof AuthenticationError) { console.error('Authentication failed. Check your credentials.'); } else if (error instanceof AccessGridError) { console.error('API error:', error.message); - } else { - console.error('Unexpected error:', error.message); } } ``` -Error types: -- `AccessGridError`: Base error class for all API errors -- `AuthenticationError`: Thrown when authentication fails (e.g., invalid credentials) - ## Requirements - Node.js 12 or higher -- Modern browser environment with support for: - - Fetch API - - Web Crypto API - - Promises - - async/await - -## Security - -The SDK automatically handles: -- Request signing using HMAC-SHA256 -- Secure payload encoding -- Authentication headers -- HTTPS communication - -Never expose your `secretKey` in client-side code. Always use environment variables or a secure configuration management system. +- Modern browser environment with support for Fetch API, Web Crypto API, async/await ## License -MIT License - See LICENSE file for details. \ No newline at end of file +MIT License - See LICENSE file for details. + +## Feature Matrix + +| Endpoint | Method | Status | +|---|---|:---:| +| POST /v1/key-cards (issue) | `accessCards.provision()` | Y | +| GET /v1/key-cards/{id} | `accessCards.get()` | Y | +| PATCH /v1/key-cards/{id} | `accessCards.update()` | Y | +| GET /v1/key-cards (list) | `accessCards.list()` | Y | +| POST .../suspend | `accessCards.suspend()` | Y | +| POST .../resume | `accessCards.resume()` | Y | +| POST .../unlink | `accessCards.unlink()` | Y | +| POST .../delete | `accessCards.delete()` | Y | +| POST /v1/console/card-templates | `console.createTemplate()` | Y | +| PUT /v1/console/card-templates/{id} | `console.updateTemplate()` | Y | +| GET /v1/console/card-templates/{id} | `console.readTemplate()` | Y | +| GET .../logs | `console.eventLog()` | Y | +| GET /v1/console/pass-template-pairs | `console.listPassTemplatePairs()` | Y | +| GET /v1/console/ledger-items | `console.ledgerItems()` | Y | +| POST .../ios_preflight | `console.iosPreflight()` | Y | +| POST /v1/console/hid/orgs | `console.hid.orgs.create()` | Y | +| GET /v1/console/hid/orgs | `console.hid.orgs.list()` | Y | +| POST /v1/console/hid/orgs/activate | `console.hid.orgs.activate()` | Y | +| Webhooks (payload) | CloudEvents receiver | Y |