├── examples ├── config.sample.json ├── custom-sidebar │ ├── sidebar.html │ ├── index.js │ └── index.html ├── oauth2 │ ├── index.html │ ├── oauth2.html │ └── index.js ├── basic │ ├── index.html │ └── index.js ├── webhooks │ ├── index.js │ └── index.html ├── utils.js └── create-appointment │ ├── index.html │ └── index.js ├── .gitignore ├── package.json ├── LICENSE ├── src ├── AcuityScheduling.js ├── AcuitySchedulingOAuth.js └── index.js └── README.md /examples/config.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "userId": 1, 3 | "apiKey": "abc123" 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | examples/oauth2/config.json 3 | examples/config.json 4 | -------------------------------------------------------------------------------- /examples/custom-sidebar/sidebar.html: -------------------------------------------------------------------------------- 1 |

Callback Example:

2 |
{{details}}
3 | -------------------------------------------------------------------------------- /examples/oauth2/index.html: -------------------------------------------------------------------------------- 1 |

Hello World

2 | 3 | Log In to Acuity 4 | -------------------------------------------------------------------------------- /examples/oauth2/oauth2.html: -------------------------------------------------------------------------------- 1 |

OAuth2 Access

2 | 3 |

Callback Query:

4 |
{{query}}
5 | 6 | {{#if query.error}} 7 |

An error has occurred: ' + query.error + '.

' 8 | {{else}} 9 |

Token Response:

10 |
{{tokenResponse}}
11 | 12 |

GET /me:

13 |
{{me}}
14 | {{/if}} 15 | -------------------------------------------------------------------------------- /examples/basic/index.html: -------------------------------------------------------------------------------- 1 |

Acuity Hello World

2 | 3 |

4 | Hi there! This is the Hello World of our examples. It will walk you through 5 | a basic connection to the Acuity API with a single account. 6 |

7 | 8 |

POST /api/v1/blocks:

9 |
{{me}}
10 | 11 |

POST /api/v1/blocks:

12 |
{{block}}
13 | 14 |

GET /api/v1/appointments?max=1

15 |
{{appointments}}
16 | -------------------------------------------------------------------------------- /examples/webhooks/index.js: -------------------------------------------------------------------------------- 1 | // Deps 2 | var express = require('express'); 3 | var config = require('../config'); 4 | var utils = require('../utils'); 5 | var Acuity = require('../../'); 6 | 7 | 8 | // App: 9 | var app = express(); 10 | 11 | 12 | // Verification middleware for the webhook route 13 | var secret = config.apiKey; 14 | var verifyMiddleware = express.urlencoded({ 15 | verify: Acuity.bodyParserVerify(secret) 16 | }); 17 | 18 | 19 | // Router: 20 | app.get('/', function (req, res) { 21 | res.render('index.html'); 22 | }); 23 | 24 | app.post('/webhook', verifyMiddleware, function (req, res) { 25 | // The message is authentic: 26 | console.log("The message is authentic:\n" + JSON.stringify(req.body, null, ' ')); 27 | res.send(''); 28 | }); 29 | 30 | 31 | // Server: 32 | utils.configure(app, {views: __dirname}); 33 | var server = utils.start(app); 34 | -------------------------------------------------------------------------------- /examples/custom-sidebar/index.js: -------------------------------------------------------------------------------- 1 | // Deps 2 | var express = require('express'); 3 | var config = require('../config'); 4 | var utils = require('../utils'); 5 | var Acuity = require('../../'); 6 | 7 | 8 | // App: 9 | var app = express(); 10 | 11 | 12 | // Verification middleware for the webhook route 13 | var secret = config.apiKey; 14 | var verifyMiddleware = express.urlencoded({ 15 | verify: Acuity.bodyParserVerify(secret) 16 | }); 17 | 18 | 19 | // Router: 20 | app.get('/', function (req, res) { 21 | res.render('index.html'); 22 | }); 23 | 24 | app.post('/custom-sidebar', verifyMiddleware, function (req, res) { 25 | setTimeout(function () { 26 | res.render('sidebar.html', { 27 | details: JSON.stringify(req.body, null, ' ') 28 | }); 29 | }, 500); 30 | }); 31 | 32 | 33 | // Server: 34 | utils.configure(app, {views: __dirname}); 35 | var server = utils.start(app); 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "acuityscheduling", 3 | "version": "0.1.9", 4 | "description": "Acuity Scheduling JS Dev Kit", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Carl Sutherland ", 10 | "license": "MIT", 11 | "keywords": [ 12 | "acuity", 13 | "acuity scheduling", 14 | "api", 15 | "oauth", 16 | "oauth2", 17 | "scheduling api", 18 | "sdk" 19 | ], 20 | "homepage": "https://github.com/acuityscheduling/acuity-js", 21 | "bugs": { 22 | "url": "https://github.com/acuityscheduling/acuity-js/issues" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/acuityscheduling/acuity-js.git" 27 | }, 28 | "engines": { 29 | "node": ">=0.8.0" 30 | }, 31 | "dependencies": { 32 | "request": "^2.66.0" 33 | }, 34 | "devDependencies": { 35 | "express": "^4.16.0", 36 | "express-session": "^1.12.1", 37 | "hbs": "^4.0.4" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/custom-sidebar/index.html: -------------------------------------------------------------------------------- 1 |

Acuity Custom Sidebar

2 | 3 |

4 | Acuity will post to /custom-sidebar when appointment details are 5 | viewed and display the result in the appointment details sidebar. 6 |

7 | 8 |

9 | Acuity won't be able to access 127.0.0.1:8000 so you'll either 10 | have to take our word for it, or try this on your server! Set your callback 11 | location under the 12 | Custom Sidebar integration. 13 |

14 | 15 |

Arguments

16 | 17 |

18 | All arguments are sent as application/x-www-form-urlencoded content. 19 |

20 | 21 | 28 | -------------------------------------------------------------------------------- /examples/webhooks/index.html: -------------------------------------------------------------------------------- 1 |

Acuity Webhook Example

2 | 3 |

4 | Acuity will post to /webhook when a new appointment is scheduled 5 | or an existing appointment is rescheduled or canceled. 6 |

7 | 8 |

9 | Acuity won't be able to access 127.0.0.1:8000 so you'll either 10 | have to take our word for it, or try this on your server! Set your callback 11 | location under the 12 | Webhook integration. 13 |

14 | 15 |

Arguments

16 | 17 |

18 | All arguments are sent as application/x-www-form-urlencoded content. 19 |

20 | 21 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Acuity Scheduling, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/utils.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var session = require('express-session'); 3 | var hbs = require('hbs'); 4 | 5 | /** 6 | * Configure Express app 7 | */ 8 | function configure (app, options) { 9 | app.set('view engine', 'html'); 10 | app.set('views', options.views); 11 | app.engine('html', hbs.__express); 12 | app.use(express.urlencoded({ 13 | extended: false 14 | })); 15 | app.use(session({ 16 | secret: 'pwnz0rz', 17 | saveUninitialized: true, 18 | resave: false 19 | })); 20 | return app; 21 | } 22 | 23 | /** 24 | * Start server 25 | */ 26 | function start (app) { 27 | 28 | var port = process.env.PORT || 8000; 29 | var server = app.listen(port, function () { 30 | console.log('Listening on %s', port); 31 | }); 32 | 33 | server.on('error', function (e) { 34 | if (e.code === 'EADDRINUSE') { 35 | console.error('Error listening on %s', port); 36 | } else { 37 | console.error(e); 38 | } 39 | }); 40 | 41 | return server; 42 | } 43 | 44 | /** 45 | * Exports: 46 | */ 47 | module.exports = { 48 | express: function (options) 49 | { 50 | return configure(express(), options); 51 | }, 52 | configure: configure, 53 | start: start 54 | }; 55 | -------------------------------------------------------------------------------- /examples/basic/index.js: -------------------------------------------------------------------------------- 1 | // Deps: 2 | var config = require('../config'); 3 | var Acuity = require('../../'); 4 | var utils = require('../utils'); 5 | 6 | 7 | // App: 8 | var app = utils.express({views: __dirname}); 9 | 10 | 11 | // Router: 12 | app.get('/', function (req, res) { 13 | 14 | var acuity = Acuity.basic(config); 15 | var response = res; 16 | 17 | acuity.request('/me', function (err, res, me) { 18 | if (err) return console.error(err); 19 | 20 | var blocksOptions = { 21 | method: 'POST', 22 | body: { 23 | start: '2015-12-24', 24 | end: '2015-12-26', 25 | calendarID: 1, 26 | notes: 'Christmas!' 27 | } 28 | }; 29 | acuity.request('/blocks', blocksOptions, function (err, res, block) { 30 | var appointmentsOptions = { 31 | qs: { 32 | max: 1 33 | } 34 | }; 35 | acuity.request('/appointments', appointmentsOptions, function (err, res, appointments) { 36 | 37 | response.render('index.html', { 38 | me: JSON.stringify(me, null, ' '), 39 | block: JSON.stringify(block, null, ' '), 40 | appointments: JSON.stringify(appointments, null, ' ') 41 | }); 42 | }); 43 | }); 44 | }); 45 | }); 46 | 47 | 48 | // Server: 49 | var server = utils.start(app); 50 | -------------------------------------------------------------------------------- /src/AcuityScheduling.js: -------------------------------------------------------------------------------- 1 | /** 2 | * AcuityScheduling Class 3 | */ 4 | 5 | var request = require('request'); 6 | var pkg = require('../package'); 7 | 8 | function AcuityScheduling (config) { 9 | 10 | config = config || {}; 11 | 12 | this.base = config.base || AcuityScheduling.base; 13 | this.apiKey = config.apiKey; 14 | this.userId = config.userId; 15 | 16 | return this; 17 | } 18 | 19 | AcuityScheduling.base = 'https://acuityscheduling.com'; 20 | AcuityScheduling.agent = 'AcuityScheduling-js/' + pkg.version; 21 | 22 | AcuityScheduling.prototype._request = function (path, options, cb) { 23 | 24 | options = options || {}; 25 | if (!cb) { 26 | cb = (typeof options === 'function') ? options : function () {}; 27 | } 28 | 29 | path = typeof path === 'string' ? path : ''; 30 | var config = { 31 | url: this.base + '/api/v1' + (path.charAt(0) === '/' ? '' : '/') + path, 32 | json: true 33 | }; 34 | 35 | // Set configurable options: 36 | if (options.auth) config.auth = options.auth; 37 | if (options.body) config.body = options.body; 38 | if (options.method) config.method = options.method; 39 | if (options.qs) config.qs = options.qs; 40 | config.headers = options.headers || {}; 41 | 42 | // User agent: 43 | config.headers['User-Agent'] = AcuityScheduling.agent; 44 | 45 | return request(config, function (err, response, body) { 46 | if (err) return cb(err, response, body); 47 | cb(err, response, body); 48 | }); 49 | }; 50 | 51 | AcuityScheduling.prototype.request = function (path, options, cb) { 52 | options = options || {}; 53 | options.auth = options.auth || { 54 | user: this.userId + '', 55 | pass: this.apiKey 56 | }; 57 | return this._request(path, options, cb); 58 | }; 59 | 60 | module.exports = AcuityScheduling; 61 | 62 | -------------------------------------------------------------------------------- /examples/oauth2/index.js: -------------------------------------------------------------------------------- 1 | // Deps 2 | var utils = require('../utils'); 3 | var config = require('../config'); 4 | var Acuity = require('../../'); 5 | 6 | 7 | // App: 8 | var app = utils.express({views: __dirname}); 9 | 10 | 11 | // Router: 12 | app.get('/', function (req, res) { 13 | res.render('index.html'); 14 | }); 15 | 16 | app.get('/authorize', function (req, res) { 17 | // Redirect the user to the Acuity authorization endpoint. You must 18 | // choose a scope to work with. 19 | var acuity = Acuity.oauth(config); 20 | acuity.authorizeRedirect(res, {scope: 'api-v1'}); 21 | }); 22 | 23 | app.get('/oauth2', function (req, res) { 24 | 25 | var options = Object.create(config); 26 | options.accessToken = config.accessToken || req.session.accessToken; 27 | var acuity = Acuity.oauth(options); 28 | var response = res; 29 | var query = req.query; 30 | 31 | if (!query.code || query.error) { 32 | response.render('oauth2.html', { 33 | error: query.error, 34 | query: JSON.stringify(query, null, ' ') 35 | }); 36 | } 37 | 38 | // Exchange the authorization code for an access token and store it 39 | // somewhere. You'll need to pass it to the AcuitySchedulingOAuth 40 | // constructor to make calls later on. 41 | acuity.requestAccessToken(query.code, function (err, tokenResponse) { 42 | 43 | if (err) return console.error(err); 44 | 45 | // Store that access token somewhere: 46 | if (tokenResponse.access_token) { 47 | req.session.accessToken = tokenResponse.access_token; 48 | } 49 | 50 | // Make a sample request: 51 | acuity.request('me', function (err, res, me) { 52 | 53 | if (err) return console.error(err); 54 | 55 | response.render('oauth2.html', { 56 | query: JSON.stringify(query, null, ' '), 57 | tokenResponse: JSON.stringify(tokenResponse, null, ' '), 58 | me: JSON.stringify(me, null, ' ') 59 | }); 60 | }); 61 | }); 62 | }); 63 | 64 | 65 | // Server: 66 | var server = utils.start(app); 67 | -------------------------------------------------------------------------------- /src/AcuitySchedulingOAuth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * AcuitySchedulingOAuth Class 3 | */ 4 | 5 | var AcuityScheduling = require('./AcuityScheduling'); 6 | var querystring = require('querystring'); 7 | var request = require('request'); 8 | 9 | function AcuitySchedulingOAuth (config) { 10 | 11 | config = config || {}; 12 | 13 | this.base = config.base || AcuityScheduling.base; 14 | this.clientId = config.clientId; 15 | this.clientSecret = config.clientSecret; 16 | this.redirectUri = config.redirectUri; 17 | this.accessToken = config.accessToken || null; 18 | 19 | return this; 20 | } 21 | 22 | AcuitySchedulingOAuth.prototype = Object.create(AcuityScheduling.prototype); 23 | 24 | AcuitySchedulingOAuth.prototype.getAuthorizeUrl = function (params) { 25 | 26 | params = params || {}; 27 | 28 | if (!params.scope) { 29 | console.error('Missing `scope` parameter.'); 30 | } 31 | 32 | var query = { 33 | response_type: 'code', 34 | scope: params.scope, 35 | client_id: this.clientId, 36 | redirect_uri: this.redirectUri 37 | }; 38 | 39 | if (params.state) { 40 | query.state = params.state; 41 | } 42 | 43 | return this.base + '/oauth2/authorize' + '?' + querystring.stringify(query); 44 | }; 45 | 46 | AcuitySchedulingOAuth.prototype.authorizeRedirect = function (res, params) { 47 | res.writeHead(302, {'location': this.getAuthorizeUrl(params)}); 48 | res.send(); 49 | }; 50 | 51 | AcuitySchedulingOAuth.prototype.requestAccessToken = function (code, cb) { 52 | 53 | var that = this; 54 | var options = { 55 | headers: { 56 | 'User-Agent': AcuityScheduling.agent 57 | }, 58 | form: { 59 | grant_type: 'authorization_code', 60 | code: code, 61 | redirect_uri: this.redirectUri, 62 | client_id: this.clientId, 63 | client_secret: this.clientSecret 64 | } 65 | }; 66 | 67 | return request.post(this.base + '/oauth2/token', options, function (err, response, body) { 68 | if (err) return cb(err); 69 | var tokenResponse = JSON.parse(body); 70 | if (tokenResponse.access_token) { 71 | that.accessToken = tokenResponse.access_token; 72 | } 73 | cb(err, tokenResponse); 74 | }); 75 | }; 76 | 77 | AcuitySchedulingOAuth.prototype.isConnected = function () { 78 | return this.accessToken ? true : false; 79 | }; 80 | 81 | AcuitySchedulingOAuth.prototype.request = function (path, options, cb) { 82 | options = options || {}; 83 | var headers = options.headers = options.headers || {}; 84 | headers.Authorization = headers.Authorization || 'Bearer ' + this.accessToken; 85 | return this._request(path, options, cb); 86 | }; 87 | 88 | module.exports = AcuitySchedulingOAuth; 89 | -------------------------------------------------------------------------------- /examples/create-appointment/index.html: -------------------------------------------------------------------------------- 1 |

Create an Appointment

2 | 3 |

4 | This example walks you through booking an appointment with our create-appointment API. 5 | First we fetch appointment types and prompt the user to choose one. Using the 6 | selected appointment type, we check available dates and 7 | times from the API and ask the user to choose. Finally, basic client 8 | information is collected and we create the appointment. 9 |

10 | 11 | {{#if appointment}} 12 |

Appointment:

13 |
14 | {{appointment}}
15 | 
16 | Start Over 17 | {{else}} 18 | 19 |
20 | {{#if start}} 21 | 22 | {{else}} 23 | {{#if appointmentTypes}} 24 |

Appointment Type: 25 | 31 |

32 | {{else}} 33 |

Appointment Type: {{appointmentType.name}}

34 | {{#if dates}} 35 |

Date: 36 | 42 |

43 | {{else}} 44 |

Date: {{date}}

45 | {{#if times}} 46 |

Time: 47 | 53 |

54 | {{else}} 55 |

Time: {{time}}

56 |

Client Info:

57 |
58 | 59 | 60 |
61 |
62 | 63 | 64 |
65 |
66 | 67 | 68 |
69 | {{/if}} 70 | {{/if}} 71 | {{/if}} 72 | 73 |

74 | 75 | Reset 76 |

77 | 78 | {{/if}} 79 |
80 | 81 | {{/if}} 82 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Acuity lib 3 | */ 4 | 5 | var AcuityScheduling = require('./AcuityScheduling'); 6 | var AcuitySchedulingOAuth = require('./AcuitySchedulingOAuth'); 7 | var querystring = require('querystring'); 8 | var crypto = require('crypto'); 9 | 10 | var acuity = { 11 | 12 | basic: function (config) { 13 | return new AcuityScheduling(config); 14 | }, 15 | 16 | oauth: function (config) { 17 | return new AcuitySchedulingOAuth(config); 18 | }, 19 | 20 | verifyMessageSignature: function (secret, body, signature) { 21 | 22 | if (!secret || typeof secret !== 'string') { 23 | throw new Error('Verify the message signature using your API key as the secret.'); 24 | } 25 | 26 | // Get hash of message using shared secret: 27 | var hasher = crypto.createHmac('sha256', secret); 28 | hasher.update(body); 29 | var hash = hasher.digest('base64'); 30 | 31 | // Compare hash to Acuity signature: 32 | if (hash !== signature) { 33 | throw new Error('This message was forged!'); 34 | } 35 | }, 36 | 37 | bodyParserVerify: function (secret) { 38 | return function (req, res, buf, encoding) { 39 | var body = buf.toString(); 40 | var signature = req.headers['X-Acuity-Signature'.toLowerCase()]; 41 | acuity.verifyMessageSignature(secret, body, signature); 42 | }; 43 | }, 44 | 45 | /** 46 | * Generate embed code for $owner. 47 | * 48 | * @param {number} owner The owner's id. 49 | * @param {object} options Additional options. 50 | * - width Iframe width 51 | * - height Iframe height 52 | * - query Query string arguments 53 | */ 54 | getEmbedCode: function (owner, options) { 55 | 56 | options = Object.create(options || {}); 57 | options.height = options.height || '800'; 58 | options.width = options.width || '100%'; 59 | 60 | var query = options.query = options.query || {}; 61 | query.owner = query.owner || owner; 62 | 63 | // Encode options: 64 | for (key in options) { 65 | if (key === 'query') { 66 | options[key] = querystring.stringify(options[key]); 67 | } else { 68 | options[key] = escape(options[key]); 69 | } 70 | } 71 | 72 | return '' + 73 | '' + 74 | ''; 75 | } 76 | }; 77 | 78 | /** 79 | * Escape HTML entities 80 | * 81 | * Escape function borrowed from Mustache. 82 | */ 83 | var enitites = { 84 | "&": "&", 85 | "<": "<", 86 | ">": ">", 87 | '"': '"', 88 | "'": ''', 89 | "/": '/' 90 | }; 91 | function escape (s) { 92 | return (s + '').replace(/[&<>"'\/]/g, function (c) { 93 | return entities[c]; 94 | }); 95 | } 96 | 97 | module.exports = acuity; 98 | -------------------------------------------------------------------------------- /examples/create-appointment/index.js: -------------------------------------------------------------------------------- 1 | // Deps: 2 | var config = require('../config'); 3 | var Acuity = require('../../'); 4 | var utils = require('../utils'); 5 | 6 | 7 | // App: 8 | var app = utils.express({views: __dirname}); 9 | 10 | 11 | // Router: 12 | app.get('/', function (request, response) { 13 | var acuity = Acuity.basic(config); 14 | var session = request.session; 15 | 16 | session.appointmentTypes = null; 17 | session.appointmentTypeID = null; 18 | session.date = null; 19 | session.time = null; 20 | 21 | response.render('index.html', { 22 | start: true 23 | }); 24 | }); 25 | 26 | app.post('/', function (request, response) { 27 | 28 | var body = request.body; 29 | var acuity = Acuity.basic(config); 30 | var session = request.session; 31 | var appointmentType = null; 32 | var appointmentTypes = session.appointmentTypes || null; 33 | var appointmentTypeID = session.appointmentTypeID = body.appointmentTypeID || session.appointmentTypeID || null; 34 | var date = session.date = body.date || session.date || null; 35 | var time = session.time = body.time || session.time || null; 36 | 37 | // First fetch possible appointment types: 38 | if (!appointmentTypes) { 39 | return acuity.request('/appointment-types', function (err, res, appointmentTypes) { 40 | request.session.appointmentTypes = appointmentTypes; 41 | response.render('index.html', { 42 | appointmentTypes: appointmentTypes 43 | }); 44 | }); 45 | } 46 | 47 | // Grab the selected appointment type: 48 | appointmentTypes.forEach(function (type) { 49 | if (type.id == appointmentTypeID) { 50 | appointmentType = appointmentType = type; 51 | } 52 | }); 53 | 54 | // Appointment type id: 55 | if (!appointmentType) { 56 | return response.render('index.html', { 57 | appointmentTypes: appointmentTypes 58 | }); 59 | } 60 | 61 | // Date: 62 | if (!date) { 63 | var month = new Date(); 64 | month = month.getFullYear() + '-' + (month.getMonth() + 1); 65 | return acuity.request('/availability/dates?month=' + month + '&appointmentTypeID=' + appointmentType.id, function (err, res, dates) { 66 | response.render('index.html', { 67 | appointmentType: appointmentType, 68 | dates: dates 69 | }); 70 | }); 71 | } 72 | 73 | // Time: 74 | if (!time) { 75 | return acuity.request('/availability/times?date=' + date + '&appointmentTypeID=' + appointmentType.id, function (err, res, times) { 76 | response.render('index.html', { 77 | appointmentType: appointmentType, 78 | date: date, 79 | times: times 80 | }); 81 | }); 82 | } 83 | 84 | // Client info: 85 | if (!body.firstName || !body.lastName || !body.email) { 86 | return response.render('index.html', { 87 | appointmentType: appointmentType, 88 | date: date, 89 | time: time 90 | }); 91 | } 92 | 93 | // Create appointment: 94 | var options = { 95 | method: 'POST', 96 | body: { 97 | appointmentTypeID: appointmentTypeID, 98 | datetime: time, 99 | firstName: body.firstName, 100 | lastName: body.lastName, 101 | email: body.email 102 | } 103 | }; 104 | return acuity.request('/appointments', options, function (err, res, appointment) { 105 | response.render('index.html', { 106 | appointment: JSON.stringify(appointment, null, ' ') 107 | }); 108 | }); 109 | }); 110 | 111 | 112 | // Server: 113 | var server = utils.start(app); 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Acuity Scheduling API - JS Tool Kit 2 | 3 | Welcome to the Acuity Scheduling JS SDK. This SDK provides examples and a standard library for integrating with the [Acuity Scheduling API](https://acuityscheduling.com/) using JS. You can learn more about developing for Acuity Scheduling at [developers.acuityscheduling.com](https://developers.acuityscheduling.com/). 4 | 5 | ## Installation 6 | 7 | This package can be installed for node using npm: 8 | 9 | ```sh 10 | $ npm install --save acuityscheduling 11 | ``` 12 | 13 | Then require it in your app: 14 | 15 | ```js 16 | var Acuity = require('acuityscheduling'); 17 | ``` 18 | 19 | Currently our API is only for server-side access and our SDK won't work on the client-side. 20 | 21 | ## Hello World 22 | 23 | Here's a basic example to get started. Just set your API credentials and run: 24 | 25 | ```js 26 | var Acuity = require('acuityscheduling'); 27 | var userId = null; 28 | var apiKey = null; 29 | 30 | var acuity = Acuity.basic({ 31 | userId: userId, 32 | apiKey: apiKey 33 | }); 34 | 35 | acuity.request('/appointments', function (err, res, appointments) { 36 | if (err) return console.error(err); 37 | console.log(appointments); 38 | }); 39 | ``` 40 | 41 | ## Examples 42 | 43 | You'll find several examples of different Acuity integrations in the [examples/](examples/) directory. These examples cover: 44 | * [Basic API Access](#basic-api-access) 45 | * [OAuth2 API Access](#oauth2-api-access) 46 | * [Webhooks](#webhooks) 47 | * [Custom Sidebar](#custom-sidebar) 48 | 49 | ##### Sample `examples/config.json` 50 | 51 | Create a config file with your API credentials to get started. All examples 52 | share a common config file containing your Acuity `userId` and `apiKey` for basic API access and verifying callbacks. [OAuth2 examples](#oauth2-api-access) require 53 | additional OAuth2 client account credentials. 54 | 55 | ```json 56 | { 57 | "userId": 1, 58 | "apiKey": "abc123" 59 | } 60 | ``` 61 | 62 | ### Basic API Access 63 | 64 | [examples/basic/](examples/basic) is a basic API integration for a single account. 65 | 66 | Start the example server by doing `PORT=8000 node examples/basic/index.js` and navigate to 127.0.0.1:8000 67 | 68 | ### Create an Appointment 69 | 70 | [examples/create-appointment/](examples/create-appointment) is a more advanced API example for scheduling an appointment. In this example, you'll see how to: 71 | 72 | * fetch appoinment types 73 | * find an available date and time 74 | * create the appointment 75 | 76 | Start the example server by doing `PORT=8000 node examples/create-appointment/index.js` and navigate to 127.0.0.1:8000 77 | 78 | ### OAuth2 API Access 79 | 80 | [examples/oauth2/](examples/oauth2) is an OAuth2 API integration. Use this to get connected with multiple Acuity accounts. 81 | 82 | Create a config file with your OAuth2 credentials to get started. If you don't have OAuth2 credentials, please fill out this registration form. 83 | Start the example server by doing `PORT=8000 node examples/oauth2/index.js` and navigate to 127.0.0.1:8000 84 | 85 | ##### Sample `examples/config.json` 86 | ```json 87 | { 88 | "clientId": "N4HgVZbjHVp3HAkR", 89 | "clientSecret": "H33vYz88sEiKVbl7EMob1URDrqZrvceSCMmZJpAi", 90 | "redirectUri": "http://127.0.0.1:8000/oauth2" 91 | } 92 | ``` 93 | 94 | ### Webhooks 95 | 96 | [examples/webhooks/](examples/webhooks) is a sample webhook integration. 97 | 98 | Start the example server by doing `PORT=8000 node examples/webhooks/index.js` and navigate to 127.0.0.1:8000 99 | 100 | ### Custom Sidebar 101 | 102 | [examples/custom-sidebar/](examples/custom-sidebar) allows you to display custom information in the appointment details sidebar. 103 | 104 | Start the example server by doing `PORT=8000 node examples/custom-sidebar/index.js` and navigate to 127.0.0.1:8000 105 | --------------------------------------------------------------------------------