API Reference version v1
https://app.officernd.com/api/v1/organizations/{orgSlug}
Getting started
The OfficeRnD public API is designed to let 3rd party applications communicate with the core OfficeRnD platform.
Our API is designed to allow you easily access system entities and resources. There is a separate API resource endpoint for every platform entity. They can be listed, viewed by ID, added, updated by id and deleted by id.
We provide two set of platform APIs that can help you achieve different use-cases:
- Server to server - available at /{resource}. Usually, used for integrating server-based applications.
- Authentication - token-based
- Client to server - available at /user/{resource} - Usually, used for building custom member tools, such as mobile apps, member portals, etc.
- Authentication - authenticated as a user/member. In order to call any of the User APIs, you need to provide a valid Authorization token, received by /auth/signin endpoint.
Populating queries
The OfficeRnD API allows you to request related entity information using the $populate method. For example, a visit has an associated visitor (contact information) which you can pull by id and poplate the data together with the visit entity. Those objects can be expanded inline with the $populate request parameter.
Objects that can be expanded are noted in this documentation. This parameter is available on all GET API requests. This option works for both object lists and single object requests.
Selecting fields
Often times you may not need all the fields of a given object. You can optimize your quieries by passing the $select request parameter to filter only the fields you need.
This parameter is available to all GET requests. This option works for both object lists or single object requests.
Filtering results
You can filter results of list request by adding query parameters. For example, if you want to find all members which are part of a company you can add team=<teamId> in the query string.
Wildcard matches
You can use wildcard matches on some string fields if you want to find a group of entites. Use $sw, $ew, $cs for starts with, ends with and contains matches. If you need to perform case insensitive search, just use $swi, $ewi and $csi.
For example, if you want to find all members with name starting with "john" no matter the case you can pass name.$swi=john.
Please, note that as these are query parameters their values need to be URL encoded.
Entity Schema
Please, note that as OfficeRnD is evolving as the leading Coworking and Flexible Workspace Management Platform, we're doing our best to document all available system entities and their properties that we believe are important for external usage.
We explicitly may not document some properties that we believe should be used only internally. We will strongly recomend that you don't use fields that are not documented here as we might change them in the future. We can guarantee future API compatibility only for documented entities and their properties.
Authentication
OAuth 2
OAuth 2 Authentication starts with creating an app in an OfficeRnD account. You'll then use the Client ID and Client Secret from that app to retrieve an access token. Applications are created from organisation owners so you need to contact the respective owner and ask them to create an application for you. Then, they will need to send you the Client Id and Client secret for the newly creted application.Scopes
The following scopes are available when requesting a token.officernd.api.read
Provides read (GET) access to the OfficeRnD app endpoints for a particular organisation.officernd.api.write
Provides write (PUT, POST, DELETE) access to the OfficeRnD app endpoints for a particular organisation.Token Request
Pre-requisites for a successful token request.Token Url
https://identity.officernd.com/oauth/tokenMethod
HTTP method POST must be used when making token requests.Content-Type header
Content-Type header must be set to application/x-www-form-urlencoded.Body
Body must contain the following fields:- client_id - the client id associated with your application provided by the organisation owner
- client_secret - the client secret associated with your application provided by the organisation owner
- grant_type - grant type to be used. We support client_credentials only
- scope - scopes you are requesting concatenated into a single string devided by a blank space e.g. "officernd.api.read officernd.api.write"
Token Response
A successful token request would result in the following response:- access_token - the requested access token
- token_type - the type of the token. This will always equal 'Bearer'
- expires_in - the validity of the token in seconds. By default tokens has a maximum validity of 3599 seconds
- scope - scopes which this token has access to
Example
Request
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "client_id={client_id}&client_secret={client_secret}&grant_type=client_credentials&scope=officernd.api.read officernd.api.write" https://identity.officernd.com/oauth/token
Response
{"access_token":"{access_token}","token_type":"Bearer","expires_in":3599,"scope":"officernd.api.read officernd.api.write"}
Authorization header
Once you have the token you need to add an Authorization header to every request. The value of the header should be "Bearer <access_token>". It should look like this:
Authorization: Bearer <access_token>
Offices
Office objects represent the separate physical locations in the organization. They are used to separate resources and members in a single organization.
Office
Property | Type | Required | Description |
---|---|---|---|
name | string | true | The name of the plan in hand. It appears everywhere in the system. |
country | string | false | The country part of the location address. |
state | string | false | The state part of the location address. |
city | string | false | The city part of the location address. |
address | string | false | The address part of the location address. |
zip | string | false | The zip code part of the location address. |
timezone | string | false | The timezone the location is at. |
image | string | false | The URL pointing to an image assigned to this location. |
isOpen | boolean | false | If true, the location is operational; if false or ommited the location is not working - either not open yet, or suspended for some other reason. |
public | boolean | false | If true, the location is shown in the members portal; if false or ommited the location is not displayed in the members portal. |
/offices
Retrieve all locations in the space.
Query Parameters
- name: (string)
The exact name of the item you are looking for. Also, you can use this property to perform a wildcard match.
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "565748274a955c790d808c77",
"slug": "shoreditch",
"name": "Shoreditch",
"city": "London",
"image": "//dzrjcxtasfoip.cloudfront.net/user-resources/organization/-c62c.jpeg",
"address": "1 Old Street",
"timezone": null,
"isOpen": true
}, {
"_id": "57575786849afaf41b0294f9",
"slug": "soho",
"name": "Soho",
"city": "London",
"image": "//dzrjcxtasfoip.cloudfront.net/user-resources/organization/-4e45.jpeg",
"address": "",
"description": "12 Soho Square",
"isOpen": true
}]
/offices/{officeId}
Retrieve an individual location.
Get the plan with id = {officeId}
URI Parameters
- officeId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
{
"_id": "565748274a955c790d808c77",
"slug": "shoreditch",
"name": "Shoreditch",
"city": "London",
"image": "//dzrjcxtasfoip.cloudfront.net/user-resources/organization/-c62c.jpeg",
"address": "1 Old Street",
"timezone": null,
"isOpen": true
}
HTTP status code 404
Body
Type: application/json
Example:
{ "message": "Office not found" }
Floors
Represents floor data. Floors are used to clasify different resources from the same location. It may contain floorplan data as well.
Floor
Property | Type | Required | Description |
---|---|---|---|
name | string | true | The name of the floorplan - usually the floor number. |
floor | string | false | Optional floor number. |
isOpen | boolean | false | If true, the location is operational; if false or ommited the location is not working - either not open yet, or suspended for some other reason. |
office | Office | true | A reference to the location the floor is part of |
area | number | false | The floor are of this part of the building. |
targetRevenue | number | false | The target revenue for the specified floor |
/floors
Retrieve all floors in the space
Query Parameters
- office: (string)
Filter by office id.
- name: (string)
The exact name of the item you are looking for.
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "5804f7935a8e12f92e8694f2",
"floor": "2",
"name": "Floor 2",
"office": "57575786849afaf41b0294f9",
"area": 80000000,
"isOpen": true
}, {
"_id": "5804f7935a8e12f92e8694f3",
"floor": "1",
"name": "Floor 1",
"office": "57575786849afaf41b0294f9",
"area": 80000000,
"isOpen": true
}, {
"_id": "5804f7935a8e12f92e8694f4",
"floor": "3",
"name": "Floor 3",
"office": "565748274a955c790d808c77",
"area": 120000000,
"isOpen": true
}, {
"_id": "5accc481714ff61400fa81d8",
"floor": "2",
"name": "new floor plan",
"area": 213676978.32467315,
"office": "57575786849afaf41b0294f9",
"isOpen": true
}, {
"_id": "5b3b936a1a2419150033d951",
"name": "Floor 4",
"office": "565748274a955c790d808c77",
"floor": "3",
"isOpen": true
}]
/floors/{floorId}
Retrieve an individual floor.
Get the plan with id = {floorId}
URI Parameters
- floorId: required (string)
Members
Member objects allow you to keep your member's data in a single place, track memberships, invoices, charges, fees, and etc. Everything that is associated with the same customer.
The API allows you to create, delete, and update your members. You can retrieve individual members as well as a list of all your members.
Here is the list of all properties the member object has:
Member
Property | Type | Required | Description |
---|---|---|---|
name | string | true | The full name of the member |
string | true | The email address of the member | |
phone | string | false | The primary phone number of the member |
twitterHandle | string | false | The twitter handle of the member |
team | id | false | Reference to the team the member belongs to |
contactPerson | boolean | false | True if the member is a contact persion in its company |
billingPerson | boolean | false | True if the member is the billing person of its company and recieves all the invoices |
office | id | false | Reference to the location the member belongs to. It will default to the only location if left empty |
paymentDetails | array | readonly | Array containing all the names of the payment details added to the member account |
createdAt | date | readonly | The date when the member has been created at. |
createdBy | id | readonly | The user that created the member. |
modifiedAt | date | readonly | The date when the member has been modified for the last time. Before the first modification, this field equals to the createdAt field. |
modifiedBy | id | readonly | The user that did the last modification to the member. Before the first modification, this field equals to the createdBy field. |
status | enum | false | The member status. It could be active, contact or former |
calcutedStatus | enum | readonly | The actual member status calculated by the platform. Please, refer to this article for more information |
/members
Retrieve all members.
Query Parameters
- modifiedAt.$gt: (date)
All items created or last modified after the pointed date
- modifiedAt.$lt: (date)
All items created or last modified before the pointed date
- createdAt.$gt: (date)
All items created after the pointed date
- createdAt.$lt: (date)
All items created before the pointed date
- office: (string)
Filter by office id.
- team: (string)
Filter by team id
- name: (string)
The exact name of the item you are looking for. Also, you can use this property to perform a wildcard match.
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "57a1a8d8c334490c73b2856f",
"name": "John Doe",
"email": "john@doe.com",
"team": "565748274a955c790d808c77",
"phone": "+442130172"
}]
Add a new member.
Body
Type: application/json
Example:
[{
"name": "John Doe",
"email": "john@doe.com",
"team": "565748274a955c790d808c77",
"phone": "+442130172"
}]
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "57a1a8d8c334490c73b2856f",
"name": "John Doe",
"email": "john@doe.com",
"team": "565748274a955c790d808c77",
"phone": "+442130172"
}]
You can delete members by passing array of IDs to be deleted as request body.
Body
Type: application/json
Example:
["57a1a8d8c334490c73b2856f"]
/members/{memberId}
Retrieve an individual member.
Get the member with id = {memberId}
URI Parameters
- memberId: required (string)
The Id of the corresponding member
Update the member with id = {memberId}
URI Parameters
- memberId: required (string)
The Id of the corresponding member
Body
Type: application/json
Example:
{
"name": "John Smith",
"phone": "+441632960798",
"properties": {
"Other": "Some additional info"
}
}
HTTP status code 200
Body
Type: application/json
Example:
{
"createdAt": "2017-09-05T11:18:03.148Z",
"createdBy": "59ae87e92dc0286c3e38bd8c",
"name": "John Smith",
"description": "",
"image": null,
"phone": "+441632960798",
"email": "john.smith@gmail.com",
"office": "59a831f7385a0d1400f325f6",
"_id": "59ae87eb2dc0286c3e38bd8d",
"properties": {
"Other": "Some additional info",
"AccountNumber": "1234"
},
"details": {
"date": null,
"name": "Mr. John Smith"
},
"tags": [],
"twitterInfo": {
"imageUrl": null,
"description": ""
}
}
Teams
Team objects allow you to keep your companies' data in single place, track memberships, invoices, charges, fees, and etc.
Everything that is associated with the same customer. The API allows you to create, delete, and update your companies / teams. You can retrieve individual teams as well as a list of all your teams.
Here is the list of all properties the team object has:
Team
Property | Type | Required | Description |
---|---|---|---|
name | string | true | The full name of the team |
string | false | The email address of the team | |
twitterHandle | string | false | The twitter handle of the team |
office | id | false | Reference to the location the team belongs to. It will default to the only location if left empty |
paymentDetails | array | readonly | Array containing all the names of the payment details added to the team account |
createdAt | date | readonly | The date when the team has been created at. |
createdBy | id | readonly | The user that created the team. |
modifiedAt | date | readonly | The date when the team has been modified for the last time. Before the first modification, this field equals to the createdAt field. |
modifiedBy | id | readonly | The user that did the last modification to the team. Before the first modification, this field equals to the createdBy field. |
/teams
Retrieve all teams.
Query Parameters
- modifiedAt.$gt: (date)
All items created or last modified after the pointed date
- modifiedAt.$lt: (date)
All items created or last modified before the pointed date
- createdAt.$gt: (date)
All items created after the pointed date
- createdAt.$lt: (date)
All items created before the pointed date
- office: (string)
Filter by office id.
- name: (string)
The exact name of the item you are looking for. Also, you can use this property to perform a wildcard match.
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "57a1a8d8c334490c73b2856f",
"name": "OfficeRnD",
"email": "info@officernd.com".
"description": "Coworking management platform for prosperous communities and efficient workspaces"
"properties": {
"customProperty": "customId"
}
}]
Add new teams
Body
Type: application/json
Example:
[{
"name": "OfficeRnD",
"email": "info@officernd.com",
"description": "Coworking management platform for prosperous communities and efficient workspaces",
"properties": {
"customProperty": "customId"
}
}]
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "57a1a8d8c334490c73b2856f",
"name": "OfficeR&D",
"email": "info@officernd.com",
"description": "Coworking management platform for prosperous communities and efficient workspaces"
"properties": {
"customProperty": "customId"
}
}]
You can delete teams by passing array of IDs to be deleted as request body.
Body
Type: application/json
Example:
["57a1a8d8c334490c73b2856f"]
/teams/{teamId}
Individual team.
Retreive the team with id = {teamId}
URI Parameters
- teamId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
{
"_id": "57a1a8d8c334490c73b2856f",
"name": "OfficeR&D",
"email": "info@officernd.com",
"description": "Coworking management platform for prosperous communities and efficient workspaces"
"properties": {
"customProperty": "customId"
}
}
HTTP status code 404
Body
Type: application/json
Example:
{ "message": "Team not found" }
Update the team with id = {teamId}
URI Parameters
- teamId: required (string)
Body
Type: application/json
Example:
{
"name": "John Smith Corp",
"properties": {
"Other": "Some additional info"
}
}
HTTP status code 200
Body
Type: application/json
Example:
{
"createdAt": "2017-09-05T11:18:03.148Z",
"createdBy": "59ae87e92dc0286c3e38bd8c",
"name": "John Smith Corp",
"description": "",
"image": null,
"email": "john.smith@gmail.com",
"office": "59a831f7385a0d1400f325f6",
"_id": "59ae87eb2dc0286c3e38bd8d",
"properties": {
"Other": "Some additional info",
"AccountNumber": "1234"
},
"details": {
"date": null,
"name": "Mr. John Smith"
},
"tags": [],
"twitterInfo": {
"imageUrl": null,
"description": ""
},
"isTeam": true
}
Memberships
Membership objects allow you to assign recurring charges (price plans) to individual members or teams. The API allows you to create, delete, and update your memberships. You can retrieve individual memberships as well as a list of all your memberships.
Here is the list of all properties the membership object has:
Property | Type | Required | Description |
---|---|---|---|
name | string | true | The display name of the membership. Used to generate invoice line items as well |
price | number | true | The monthly price of the membership |
team | Team | false | Reference to the company (required if no member) |
member | Member | false | Reference to the company (required if no member) |
office | Office | true | A reference to the location the fee is issued for |
plan | Plan | true | A reference to the price plan assigned to the membership. It is used to determine the sales account when generating an invoice |
isPersonal | boolean | false | If true, the membership is is billed to the assigned member and not to the company |
startDate | date | true | The membership starting date |
endDate | date | false | The last date of membership. If not set the membership is open-ended |
discount | id | false | A reference to a discount definition |
discountAmount | number | false | Manually granted discount for the specific membership |
calculatedDiscountAmount | number | readonly | The actual discount amount comming from a discount defintion or from a manually granted discount |
discountedPrice | number | readonly | The final membership unit price after granting the discount |
locked | boolean | false | If true, prevents editing the membership |
/memberships
Retrieve all memberships.
Query Parameters
- $populate: (string)
Comma-separated list of all properties to be populated. Only id references can be populated. Refer to Populating queries section for more information.
- team: (string)
Filter by team id
- member: (string)
Filter by member id
Add multiple membership objects.
Body
Type: application/json
Example:
[{
"name": "Hotdesk",
"price": 100,
"plan": "5885f3953eefcdc52755a6a0",
"team": "565363aa3492382393f68e7c",
"office": "565748274a955c790d808c77",
"startDate": "2017-02-21T00:00:00.000Z"
}]
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "58ac916ba372d96504057a65",
"name": "Hotdesk",
"price": 100,
"plan": "5885f3953eefcdc52755a6a0",
"team": "565363aa3492382393f68e7c",
"office": "565748274a955c790d808c77",
"startDate": "2017-02-21T00:00:00.000Z"
}]
You can delete memberships by passing an array of IDs to be deleted as request body.
Body
Type: application/json
Example:
["58ac916ba372d96504057a65"]
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "58ac916ba372d96504057a65",
"name": "Hotdesk",
"price": 100,
"plan": "5885f3953eefcdc52755a6a0",
"team": "565363aa3492382393f68e7c",
"office": "565748274a955c790d808c77",
"startDate": "2017-02-21T00:00:00.000Z"
}]
/memberships/{membershipId}
Retrieve individual membership.
Get the membership with id = {membershipId}
URI Parameters
- membershipId: required (string)
Query Parameters
- $populate: (string)
Comma-separated list of all properties to be populated. Only id references can be populated. Refer to Populating queries section for more information.
Update the membership with id = {membershipId}
. The body should contain only the properties that need to be updated.
URI Parameters
- membershipId: required (string)
Body
Type: application/json
Example:
{
"price": 150
}
HTTP status code 200
Body
Type: application/json
Example:
{
"name": "Hotdesk",
"price": 150,
"plan": "5885f3953eefcdc52755a6a0",
"team": "565363aa3492382393f68e7c",
"office": "565748274a955c790d808c77",
"startDate": "2017-02-21T00:00:00.000Z"
}
Checkins
Checkins represent member presence at a certain office. The checkin, checkout and list checkins endpoints allow you to manage member presence.
Here is a list of all properties the checkin object has:
Checkin
Property | Type | Required | Description |
---|---|---|---|
member | Member | true | A reference to the member that is checked in. |
team | Team | false | A reference to the team of the member that is checked in. |
start | date | true | The start date of the checkin. |
end | date | false | The end date of the checkin. |
office | Office | true | A reference to the office where the member is checked in. |
/checkins
Retrieve all checkins.
Query Parameters
- $populate: (string)
Comma-separated list of all properties to be populated. Only id references can be populated. Refer to Populating queries section for more information.
- start.$gt: (date)
All items starting after the pointed date
- start.$lt: (date)
All items starting before the pointed date
- end.$gt: (date)
All items ending after the pointed date
- end.$lt: (date)
All items ending before the pointed date
- member: (string)
Filter by member id
- team: (string)
Filter by team id
/checkins/{memberId}
Check in member with id = {memberId}
. By default, the request checks in the member with start
date now, no end
date and the office
where the member is registered. You can optionally provide start
, end
and office
in the request body. Member checkins with start
date before the current date and no end
date are not allowed.
URI Parameters
- memberId: required (string)
Body
Type: application/json
Example:
{
"start": "2020-02-21T12:24:33.846Z",
"end": "2020-02-21T14:30:34.702Z",
"office": "5e37eb8b5bc88201b7aa5071"
}
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "5e4fcc029781c3c00d2de99d",
"member": "5e37eb9e5bc88201b7aa50db",
"team": "5e37eb8b5bc88201b7aa5077",
"start": "2020-02-21T12:24:33.846Z",
"end": "2020-02-21T14:30:34.702Z",
"office": "5e37eb8b5bc88201b7aa5071"
}]
/checkins/checkout/{memberId}
Check out member with id = {memberId}
. The request will update current checkins end
date with now if the checkins don't have an end
date.
URI Parameters
- memberId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "5e4fcc029781c3c00d2de99d",
"member": "5e37eb9e5bc88201b7aa50db",
"team": "5e37eb8b5bc88201b7aa5077",
"start": "2020-02-21T12:24:33.846Z",
"end": "2020-02-21T14:30:34.702Z",
"office": "5e37eb8b5bc88201b7aa5071"
}]
Bookings
Bookings are short-term reservation of resource. Bookings can reserve any resource, such as Meeting Room, Hot desk, Private Office, etc.
The API allows you to create, delete, and update bookings.
Here is the list of most properties the booking:
Booking
Property | Type | Required | Description |
---|---|---|---|
start.dateTime | date | true | Start datetime of the booking in ISO date time format |
end.dateTime | date | true | End datetime of the booking in ISO date time format |
timezone | string | readonly | The booking resource timezone |
source | string | false | The origin/source of the booking |
summary | string | false | Booking title/summary |
resourceId | id | true | Reference to the booked resource |
plan | id | readonly | Reference to the resource rate used for the booking |
team | id | false | Reference to the company (required if no member) |
member | id | false | Reference to the member (required if no team) |
description | string | false | Long description of the booking |
tentative | boolean | false | True if the booking is not confirmed. Tentative booking are not charged. |
free | boolean | false | True if the booking is marked as free. If create a booking marked as free, no fees will be added for it. |
fees | array | readonly | Array of items describing how the booking will be charged for. Described below as BookingFee. |
extras | object | readonly | Dictionary with keys the IDs of the extras to add to the booking and values - the quantity of the extra |
Booking Fee
Property | Type | Required | Description |
---|---|---|---|
date | date | true | The date of the occurrence the fee has been generated for |
fee | Fee | false | The actual charge generated for this occurrence. |
credits | array | true | The credits used to do the booking |
extraFees | array(Fee) | true | Array of fees generated for the extras. |
/bookings
Retrieve all bookings.
Query Parameters
- office: (string)
Filter by office id.
- member: (string)
Filter by member id
- team: (string)
Filter by team id
Add one or many bookings.
Body
Type: application/json
Example:
[{
"team": "57a1a8d8c334asdafc73b2856f",
"member": "57a1a8d8c334asdafc73safag6f",
"start": {
"dateTime": "2017-10-31T09:00:00.000Z"
},
"end": {
"dateTime": "2017-05-31T11:00:00.000Z"
},
"resourceId": "58e3414e60b481c648202848",
"summary": "First booking"
}, {
"team": "57a1a8d8c334asdafc73b2856f",
"member": "57a1a8d8c334asdafc73safag6f",
"start": {
"dateTime": "2017-05-31T13:00:00.000Z"
},
"end": {
"dateTime": "2017-05-31T14:00:00.000Z"
},
"resourceId": "58e3414e60b481c648202848",
"summary": "Second booking"
}
}]
HTTP status code 200
Body
Type: application/json
Example:
[{
"start": {
"dateTime": "2017-10-31T09:00:00.000Z"
},
"end": {
"dateTime": "2017-10-31T11:00:00.000Z"
},
"resourceId": "5943b5ebb4bd93ca5f1906fc",
"extras": {},
"recurrence": {
"rrule": null
},
"member": null,
"visitors": [],
"members": [],
"team": "59493231518f3f150d306c62",
"timezone": "Europe/London",
"source": "admin",
"plan": "5943b59eb4bd93ca5f1906f7",
"fees": [{
"date": "2017-10-31T00:00:00.000Z",
"extraFees": [],
"credits": [{
"count": 2,
"credit": "594932ea518f3f150d306c89"
}]
}],
"accountedUntil": null
}, {
"start": {
"dateTime": "2017-10-31T13:00:00.000Z"
},
"end": {
"dateTime": "2017-10-31T14:00:00.000Z"
},
"resourceId": "5943b5ebb4bd93ca5f1906fc",
"extras": {},
"recurrence": {
"rrule": null
},
"member": null,
"visitors": [],
"members": [],
"team": "59493231518f3f150d306c62",
"timezone": "Europe/London",
"source": "admin",
"plan": "5943b59eb4bd93ca5f1906f7",
"fees": [{
"date": "2017-10-31T00:00:00.000Z",
"extraFees": [],
"credits": [{
"count": 1,
"credit": "594932ea518f3f150d306c89"
}]
}],
"accountedUntil": null
}]
/bookings/checkout
Booking checkout endpoint is used to automatically pick resources for booking based on a booking request. It can be used when it doesn't matter which resource should be booked, but it is more important certain criterias to be matched.
Booking Request
Property | Type | Required | Description |
---|---|---|---|
start | date | true | Start datetime of the booking request. |
end | date | true | End datetime of the booking request. |
team | id | false | Reference to the company (required if no member). |
member | id | false | Reference to the member (required if no team). |
extras | object | false | Dictionary with keys the IDs of the extras to add to the booking and values - the quantity of the extra. |
source | string | false | The origin/source of the bookings. |
summary | string | false | Booking title/summary. |
description | string | false | Long description of the booking. |
tentative | boolean | false | True if the booking is not confirmed. Tentative booking are not charged. |
free | boolean | false | True if the booking is marked as free. If create a booking marked as free, no fees will be added for it. |
size | number* | false | The resource size to used. |
count | number | true | The count of bookings of this type to be made. |
rate | id | false | The resource rate to be used to lookup available resources. |
resourceType | id | false | The resource type to be used to lookup available resources. |
number*: You can use a query instead of a value, for example { size: { $gte: 2 } }
, meaning the size must be at least 2
Create bookings without specifing resources, but search parameters instead. The request is of type Booking Request and the result is an array of Booking.
Body
Type: application/json
Example:
[{
"start": "2017-11-11T17:00:00Z",
"end": "2017-11-11T18:00:00Z",
"count": 2,
"team": "5a1ec6b9825f901400025780",
"summary": "title of the booking",
"description": "description of the booking - book 2 meeting rooms with 5 coffees each",
"resourceType": "meeting_room",
"extras": {
"59cbbb2d863e7014004879b6": 5
}
}, {
"start": "2017-11-11T17:00:00Z",
"end": "2017-11-11T18:00:00Z",
"count": 2,
"team": "5a1ec6b9825f901400025780",
"summary": "title of the booking",
"description": "description of the booking - book 2 dedicated desks",
"resourceType": "desk"
}, {
"start": "2017-11-11T17:00:00Z",
"end": "2017-11-11T18:00:00Z",
"count": 2,
"team": "5a1ec6b9825f901400025780",
"summary": "title of the booking",
"description": "description of the booking - book 2 hotdesks",
"resourceType": "hotdesk"
}, {
"start": "2017-11-11T17:00:00Z",
"end": "2017-11-11T18:00:00Z",
"count": 2,
"team": "5a1ec6b9825f901400025780",
"summary": "title of the booking",
"description": "description of the booking - book 2 phonebooths with only rates",
"rate": "59cba40ac1798a14001ada01"
}]
HTTP status code 200
Body
Type: application/json
Example:
[{
"start": {
"dateTime": "2017-11-11T17:00:00.000Z"
},
"end": {
"dateTime": "2017-11-11T18:00:00.000Z"
},
"summary": "title of the booking",
"resourceId": "5a0c43d93237881400d15919",
"description": "description of the booking - book 2 meeting rooms with 5 coffees each",
"team": "5a1ec6b9825f901400025780",
"timezone": "Europe/London",
"plan": "59cb9e44c1798a14001ad350",
"accountedUntil": null,
"createdBy": "5a1ebb8ed75ffb140046daa8",
"createdAt": "2017-11-29T16:11:49.879Z",
"_id": "5a1edc45adae3a140069ca58",
"extras": {
"59cbbb2d863e7014004879b6": 5
},
"members": [],
"visitors": [],
"fees": [{
"date": "2017-11-11T00:00:00.000Z",
"fee": "5a1edc45adae3a140069ca46",
"extraFees": [
"5a1edc45adae3a140069ca47"
],
"credits": []
}]
},
{
"start": {
"dateTime": "2017-11-11T17:00:00.000Z"
},
"end": {
"dateTime": "2017-11-11T18:00:00.000Z"
},
"summary": "title of the booking",
"resourceId": "5a1eb96db7ca79140096c45b",
"description": "description of the booking - book 2 meeting rooms with 5 coffees each",
"team": "5a1ec6b9825f901400025780",
"timezone": "Europe/London",
"plan": "59cb9e44c1798a14001ad350",
"accountedUntil": null,
"createdBy": "5a1ebb8ed75ffb140046daa8",
"createdAt": "2017-11-29T16:11:49.879Z",
"_id": "5a1edc45adae3a140069ca59",
"extras": {
"59cbbb2d863e7014004879b6": 5
},
"members": [],
"visitors": [],
"fees": [{
"date": "2017-11-11T00:00:00.000Z",
"fee": "5a1edc45adae3a140069ca42",
"extraFees": [
"5a1edc45adae3a140069ca43"
],
"credits": []
}]
},
{
"start": {
"dateTime": "2017-11-11T17:00:00.000Z"
},
"end": {
"dateTime": "2017-11-11T18:00:00.000Z"
},
"summary": "title of the booking",
"resourceId": "59cba49cc1798a14001ada21",
"description": "description of the booking - book 2 dedicated desks",
"team": "5a1ec6b9825f901400025780",
"timezone": "Europe/London",
"accountedUntil": null,
"createdBy": "5a1ebb8ed75ffb140046daa8",
"createdAt": "2017-11-29T16:11:49.879Z",
"_id": "5a1edc45adae3a140069ca5a",
"members": [],
"visitors": [],
"fees": []
},
{
"start": {
"dateTime": "2017-11-11T17:00:00.000Z"
},
"end": {
"dateTime": "2017-11-11T18:00:00.000Z"
},
"summary": "title of the booking",
"resourceId": "59cba4a57de35214002d9fb9",
"description": "description of the booking - book 2 dedicated desks",
"team": "5a1ec6b9825f901400025780",
"timezone": "Europe/London",
"accountedUntil": null,
"createdBy": "5a1ebb8ed75ffb140046daa8",
"createdAt": "2017-11-29T16:11:49.879Z",
"_id": "5a1edc45adae3a140069ca5b",
"members": [],
"visitors": [],
"fees": []
},
{
"start": {
"dateTime": "2017-11-11T17:00:00.000Z"
},
"end": {
"dateTime": "2017-11-11T18:00:00.000Z"
},
"summary": "title of the booking",
"resourceId": "59cba4ae7de35214002d9fbc",
"description": "description of the booking - book 2 hotdesks",
"team": "5a1ec6b9825f901400025780",
"timezone": "Europe/London",
"accountedUntil": null,
"createdBy": "5a1ebb8ed75ffb140046daa8",
"createdAt": "2017-11-29T16:11:49.879Z",
"_id": "5a1edc45adae3a140069ca5c",
"members": [],
"visitors": [],
"fees": []
},
{
"start": {
"dateTime": "2017-11-11T17:00:00.000Z"
},
"end": {
"dateTime": "2017-11-11T18:00:00.000Z"
},
"summary": "title of the booking",
"resourceId": "59cba4b5c1798a14001ada25",
"description": "description of the booking - book 2 hotdesks",
"team": "5a1ec6b9825f901400025780",
"timezone": "Europe/London",
"plan": "5a1e90b10d8de714006d2e85",
"accountedUntil": null,
"createdBy": "5a1ebb8ed75ffb140046daa8",
"createdAt": "2017-11-29T16:11:49.879Z",
"_id": "5a1edc45adae3a140069ca5d",
"members": [],
"visitors": [],
"fees": [{
"date": "2017-11-11T00:00:00.000Z",
"fee": "5a1edc45adae3a140069ca44",
"extraFees": [],
"credits": []
}]
},
{
"start": {
"dateTime": "2017-11-11T17:00:00.000Z"
},
"end": {
"dateTime": "2017-11-11T18:00:00.000Z"
},
"summary": "title of the booking",
"resourceId": "5a1ec833825f9014000257db",
"description": "description of the booking - book 2 phonebooths with only rates",
"team": "5a1ec6b9825f901400025780",
"timezone": "Europe/London",
"plan": "59cba40ac1798a14001ada01",
"accountedUntil": null,
"createdBy": "5a1ebb8ed75ffb140046daa8",
"createdAt": "2017-11-29T16:11:49.879Z",
"_id": "5a1edc45adae3a140069ca5e",
"members": [],
"visitors": [],
"fees": [{
"date": "2017-11-11T00:00:00.000Z",
"fee": "5a1edc45adae3a140069ca45",
"extraFees": [],
"credits": []
}]
},
{
"start": {
"dateTime": "2017-11-11T17:00:00.000Z"
},
"end": {
"dateTime": "2017-11-11T18:00:00.000Z"
},
"summary": "title of the booking",
"resourceId": "5a1ec83fadae3a140069c8df",
"description": "description of the booking - book 2 phonebooths with only rates",
"team": "5a1ec6b9825f901400025780",
"timezone": "Europe/London",
"plan": "59cba40ac1798a14001ada01",
"accountedUntil": null,
"createdBy": "5a1ebb8ed75ffb140046daa8",
"createdAt": "2017-11-29T16:11:49.879Z",
"_id": "5a1edc45adae3a140069ca5f",
"members": [],
"visitors": [],
"fees": [{
"date": "2017-11-11T00:00:00.000Z",
"fee": "5a1edc45adae3a140069ca49",
"extraFees": [],
"credits": []
}]
}
]
/bookings/{bookingId}
Retrieve a booking by ID.
Get the booking with id = {bookingId}
URI Parameters
- bookingId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
{
"start": { "dateTime": "2017-07-11T11:30:00.000Z" },
"end": { "dateTime": "2017-07-11T12:00:00.000Z" },
"timezone": "Europe/London",
"source": "portal",
"recurrence": { "rrule": null },
"fees": [{ "date": "2017-07-11T00:00:00.000Z", "credits": [], "fee": { "_id": "5963c6d3d145045357a22cf1", "name": "Small Conference Room, 0.5 x £4.00 / hour - Jul 11, 2017", "price": 4, "quantity": 0.5, "invoice": { "status": "not_paid" } }, "extraFees": [{ "_id": "5963c6d3d145045357a22cf2", "name": "Extra", "price": 6, "quantity": 1, "invoice": { "status": "not_paid" } }] }],
"resourceId": "5963817ab75f6fa2450756b1",
"team": "5943fdb5772a5ce96552a488",
"member": "5911c5fefb2465366be4b157",
"visitors": [],
"members": [],
"plan": "5943b59eb4bd93ca5f1906f7",
"extras": { "59480b70b41f0dd0b54ce1b7": 1 },
"credits": [],
"extraFees": [],
"accountedUntil": null,
"_id": "5963c6d3d145045357a22cf3"
}
Update the booking with id = {bookingId}
URI Parameters
- bookingId: required (string)
Body
Type: application/json
Example:
{
"_id": "5963c6d3d145045357a22cf3",
"title": "Update"
}
HTTP status code 200
Body
Type: application/json
Example:
{
"start": { "dateTime": "2017-07-11T11:30:00.000Z", "date": null },
"end": { "dateTime": "2017-07-11T12:00:00.000Z", "date": null },
"timezone": "Europe/London",
"source": "portal",
"summary": "Update",
"resourceId": "5963817ab75f6fa2450756b1",
"team": "5943fdb5772a5ce96552a488",
"member": "5911c5fefb2465366be4b157",
"plan": "5943b59eb4bd93ca5f1906f7",
"accountedUntil": null,
"_id": "5963c6d3d145045357a22cf3",
"extraFees": [],
"credits": [],
"extras": { "59480b70b41f0dd0b54ce1b7": 1 },
"members": [],
"visitors": [],
"fees": [{ "date": "2017-07-11T00:00:00.000Z", "fee": "5964f000ca6c7ca8782cd267", "extraFees": ["5964f000ca6c7ca8782cd268"], "credits": [] }],
"recurrence": { "rrule": null }
}
Delete a booking with id = {bookingId}
. Deleting the booking will remove the assiciated fees and will return the used credits the the company/member account.
URI Parameters
- bookingId: required (string)
/bookings/summary
Validate a booking and returns if the booking is valid and any charges, fees, or credits will be used.
Booking Summary
Property | Type | Required | Description |
---|---|---|---|
target | Booking Target | false | Object, describing the member and company, which are about to create a booking. |
booking | Booking | false | The booking to be validated if possible to be created without the team and member properties (required if bookings is not passed). |
Booking Target
Property | Type | Required | Description |
---|---|---|---|
team | id | false | The ID of the team in hand |
member | id | false | The ID of the member in hand |
Body
Type: application/json
Example:
{
"booking": {
"start": {
"dateTime": "2017-05-31T09:00:00.000Z"
},
"end": {
"dateTime": "2017-05-31T11:00:00.000Z"
},
"resourceId": "58e3414e60b481c648202848",
"summary": "Meeting Room booking info"
},
"target": {
"team": "57a1a8d8c334asdafc73b2856f",
"member": "57a1a8d8c334asdafc73safag6f"
}
}
HTTP status code 200
Body
Type: application/json
Example:
{
"start": {
"dateTime": "2017-07-11T16:30:00.000Z"
},
"end": {
"dateTime": "2017-07-11T17:30:00.000Z"
},
"resourceId": "5943b5ebb4bd93ca5f1906fc",
"extras": {},
"recurrence": {
"rrule": null
},
"member": null,
"visitors": [],
"members": [],
"team": "59493231518f3f150d306c62",
"timezone": "Europe/London",
"source": "admin",
"plan": "5943b59eb4bd93ca5f1906f7",
"fees": [{
"date": "2017-07-11T00:00:00.000Z",
"extraFees": [],
"credits": [{
"count": 1,
"credit": "594932ea518f3f150d306c89"
}]
}],
"accountedUntil": null
}
HTTP status code 400
Body
Type: application/json
Example:
{
"name": "InvalidOperationError",
"message": "Resource is not available in this time slot.",
"type": "Invalid operation",
"details": {
"messages": ["Resource is not available in this time slot."]
}
}
/bookings/checkout-summary
Validate a booking request and returns fees or credits that will be used. Accepts Booking Request as body and returns an array of Booking.
Body
Type: application/json
Example:
{
"start": "2017-11-11T17:00:00Z",
"end": "2017-11-11T18:00:00Z",
"count": 2,
"team": "5a1ec6b9825f901400025780",
"summary": "title of the booking",
"description": "description of the booking - book 2 phonebooths with only rates",
"rate": "59cba40ac1798a14001ada01"
}
HTTP status code 200
Body
Type: application/json
Example:
[{
"start": {
"dateTime": "2017-11-11T17:00:00.000Z"
},
"end": {
"dateTime": "2017-11-11T18:00:00.000Z"
},
"summary": "title of the booking",
"resourceId": "5a1ec833825f9014000257db",
"description": "description of the booking - book 2 phonebooths with only rates",
"team": "5a1ec6b9825f901400025780",
"timezone": "Europe/London",
"plan": "59cba40ac1798a14001ada01",
"accountedUntil": null,
"createdBy": "5a1ebb8ed75ffb140046daa8",
"createdAt": "2017-11-29T16:11:49.879Z",
"_id": "5a1edc45adae3a140069ca5e",
"members": [],
"visitors": [],
"fees": [{
"date": "2017-11-11T00:00:00.000Z",
"fee": "5a1edc45adae3a140069ca45",
"extraFees": [],
"credits": []
}]
},
{
"start": {
"dateTime": "2017-11-11T17:00:00.000Z"
},
"end": {
"dateTime": "2017-11-11T18:00:00.000Z"
},
"summary": "title of the booking",
"resourceId": "5a1ec83fadae3a140069c8df",
"description": "description of the booking - book 2 phonebooths with only rates",
"team": "5a1ec6b9825f901400025780",
"timezone": "Europe/London",
"plan": "59cba40ac1798a14001ada01",
"accountedUntil": null,
"createdBy": "5a1ebb8ed75ffb140046daa8",
"createdAt": "2017-11-29T16:11:49.879Z",
"_id": "5a1edc45adae3a140069ca5f",
"members": [],
"visitors": [],
"fees": [{
"date": "2017-11-11T00:00:00.000Z",
"fee": "5a1edc45adae3a140069ca49",
"extraFees": [],
"credits": []
}]
}
]
/bookings/occurrences
Get a list of all occurrences for a specified period.
Query Parameters
- office: (string)
Filter by office id.
- member: (string)
Filter by member id
- team: (string)
Filter by team id
- start: required (date)
Start of the interval to get occurrences for
- end: required (date)
End of the interval to get occurrences for
HTTP status code 200
Body
Type: application/json
Example:
[{
"start": { "dateTime": "2017-07-11T11:30:00.000Z" },
"end": { "dateTime": "2017-07-11T12:00:00.000Z" },
"timezone": "Europe/London",
"source": "portal",
"recurrence": { "rrule": null },
"fees": [{ "date": "2017-07-11T00:00:00.000Z", "credits": [], "fee": { "_id": "5963c6d3d145045357a22cf1", "name": "Small Conference Room, 0.5 x £4.00 / hour - Jul 11, 2017", "price": 4, "quantity": 0.5, "invoice": { "status": "not_paid" } }, "extraFees": [{ "_id": "5963c6d3d145045357a22cf2", "name": "Extra", "price": 6, "quantity": 1, "invoice": { "status": "not_paid" } }] }],
"resourceId": "5963817ab75f6fa2450756b1",
"team": "5943fdb5772a5ce96552a488",
"member": "5911c5fefb2465366be4b157",
"visitors": [],
"members": [],
"plan": "5943b59eb4bd93ca5f1906f7",
"extras": { "59480b70b41f0dd0b54ce1b7": 1 },
"credits": [],
"extraFees": [],
"accountedUntil": null,
"reference": "2K4SJ4M",
"bookingId": "5963c6d3d145045357a22cf3"
}]
Fees
Fee objects describe one-off charges or groups of one-off charges. They might or might not be connected to a specific plan, but must have their own name and price.
You can retrieve individual fee as well as query all the fees by location, company or member.
Here is the list of all properties the fee object has:
Fee
Property | Type | Required | Description |
---|---|---|---|
name | string | true | The name of the charge. It will appear in the invoice line item. |
price | number | true | The unit price of the item described by the fee. |
quantity | number | false | The quantity described by the one-off charge. If ommited, 1 is assumed. |
date | date | true | The date when the service has been sold. This date is used to determine in which invoice the fee should be included. |
team | Team | false | Reference to the company (required if no member) |
member | Member | false | Reference to the company (required if no member) |
office | Office | true | A reference to the location the fee is issued for |
plan | Plan | false | A reference to the price plan assigned to the fee. It is used to determine the sales account when generating an invoice |
refundable | boolean | false | If true, the one-off charge is counted as deposit and can be refuded later on. |
billInAdvance | boolean | false | If true, the one-off charge is is billed in advance (respecting its date). By default one-off charges are billed in arears. |
isPersonal | boolean | false | If true, the one-off charge is is billed to the assigned member and not to the company. |
/fees
Retrieve all fee objects
Query Parameters
- member: (string)
Filter by member id
- team: (string)
Filter by team id
- office: (string)
Filter by office id.
- $populate: (string)
Comma-separated list of all properties to be populated. Only id references can be populated. Refer to Populating queries section for more information.
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "58ac916ba372d96504057a65",
"refundable": false,
"billInAdvance": true,
"name": "Day pass",
"price": 30,
"plan": "5885f3953eefcdc52755a6a0",
"team": "565363aa3492382393f68e7c",
"office": "565748274a955c790d808c77",
"date": "2017-02-21T00:00:00.000Z",
"quantity": 5
}]
Add multiple fee objects.
Body
Type: application/json
Example:
[{
"billInAdvance": true,
"name": "Day pass",
"price": 30,
"plan": "5885f3953eefcdc52755a6a0",
"team": "565363aa3492382393f68e7c",
"office": "565748274a955c790d808c77",
"date": "2017-02-21T00:00:00.000Z",
"quantity": 5
}]
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "58ac916ba372d96504057a65",
"refundable": false,
"billInAdvance": true,
"name": "Day pass",
"price": 30,
"plan": "5885f3953eefcdc52755a6a0",
"team": "565363aa3492382393f68e7c",
"office": "565748274a955c790d808c77",
"date": "2017-02-21T00:00:00.000Z",
"quantity": 5
}]
/fees/{feeId}
Retrieve an individual fee object.
Get the fee object with id = {feeId}
URI Parameters
- feeId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
{
"_id": "58ac916ba372d96504057a65",
"refundable": false,
"billInAdvance": true,
"name": "Day pass",
"price": 30,
"plan": "5885f3953eefcdc52755a6a0",
"team": "565363aa3492382393f68e7c",
"office": "565748274a955c790d808c77",
"date": "2017-02-21T00:00:00.000Z",
"quantity": 5
}
HTTP status code 404
Body
Type: application/json
Example:
{
"message": "Fee not found"
}
Update the fee object with id = {feeId}
URI Parameters
- feeId: required (string)
Body
Type: application/json
Example:
{
"price": 35,
}
HTTP status code 200
Body
Type: application/json
Example:
{
"_id": "58ac916ba372d96504057a65",
"refundable": false,
"billInAdvance": true,
"name": "Day pass",
"price": 35,
"plan": "5885f3953eefcdc52755a6a0",
"team": "565363aa3492382393f68e7c",
"office": "565748274a955c790d808c77",
"date": "2017-02-21T00:00:00.000Z",
"quantity": 5
}
HTTP status code 404
Body
Type: application/json
Example:
{
"message": "Fee not found"
}
Remove the fee object with id = {feeId}
URI Parameters
- feeId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
{
"_id": "58ac916ba372d96504057a65",
"refundable": false,
"billInAdvance": true,
"name": "Day pass",
"price": 35,
"plan": "5885f3953eefcdc52755a6a0",
"team": "565363aa3492382393f68e7c",
"office": "565748274a955c790d808c77",
"date": "2017-02-21T00:00:00.000Z",
"quantity": 5
}
HTTP status code 404
Body
Type: application/json
Example:
{
"message": "Fee not found"
}
Plans
Plan objects (or price plans) allow you to create one-off or recurring named charges.
You can retrieve individual plans as well as a list of all your plans.
Plan
Property | Type | Required | Description |
---|---|---|---|
name | string | true | The name of the plan in hand. It appears everywhere in the system. |
description | string | false | Text describing the plan. It will appear when users need to choose among different plans. |
price | number | true | The default unit price for the plan, depending if it is recurring plan or a one-off plan. |
locations | [id] | true | List of all locations where the plan is present. If empty, all locations are assumed. |
type | enum | true | The type of the plan. It could be office, desk, hotdesk or service. |
intervalLength | enum | true | The plan interval. It could be month or once. |
code | string | false | External identified for the plan. Usually used for accounting purposes. |
NOTE: Right now the plans API returns both rates and plans, so you should ignore all items with isRate property set to true
/plans
Retrieve all plans.
Query Parameters
- name: (string)
The exact name of the item you are looking for. Also, you can use this property to perform a wildcard match.
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "",
"name": "Dedicated desk",
"description": "Fixed desk for member, suitable for full-time members",
"intervalLenght": "month",
"price": "200",
"deposit": "200",
"addOns": []
}]
/plans/{planId}
Retrieve an individual plan.
Get the plan with id = {planId}
URI Parameters
- planId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
{
"_id": "",
"name": "Dedicated desk",
"description": "Fixed desk for member, suitable for full-time members",
"intervalLenght": "month",
"price": "200",
"deposit": "200",
"addOns": []
}
HTTP status code 404
Body
Type: application/json
Example:
{"message": "Plan not found"}
Rates
Rate objects (or resource rates) describe available resource rates in the system. Rates are very similar to the plans.
/rates
Retrieve all resource rates.
Query Parameters
- name: (string)
The exact name of the item you are looking for.
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "59a832dc385a0d1400f32619",
"name": "Room Rate",
"price": 20,
"cancellationPolicy": {
"minimumPeriod": 24
},
"extras": [{
"_id": "59a832ea48185f140085680c",
"name": "Coffee",
"price": 2
}],
"maxDuration": 24
}]
/rates/{rateId}
Retrieve an individual rate.
Get the rate with id = {rateId}
URI Parameters
- rateId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
{
"_id": "59a832dc385a0d1400f32619",
"name": "Room Rate",
"price": 20,
"cancellationPolicy": {
"minimumPeriod": 24
},
"extras": [{
"_id": "59a832ea48185f140085680c",
"name": "Coffee",
"price": 2
}],
"maxDuration": 24
}
Resources
Resource objects describe all type of bookable (short term bookings or long term assignments) resources in the system.
Resource types can be extended and modified, but the default ones are:
- meeting rooms - meeting_room
- private offices - team_room
- private office desk - desk_tr
- dedicated desks - desk
- hot desks - hotdesk
You can retrieve individual resources as well as a list of all your resources or all of a specific type.
Resource
Property | Type | Required | Description |
---|---|---|---|
name | string | true | The name of the resource in hand. |
rate | Rate | false | The resource rate used for pricing bookings for this particualr resource. |
office | Office | true | The location the resource is assigned to |
room | Floor | true | The floorplan the resource is assigned to |
type | enum | true | The type of the resource. One of types listed above. |
parents | [id] | false | Array of resource ids, which are parents of the current resource. Parents are only present for resource types with hierarchicalResourcesEnabled option set to true . |
/resources
Retrieve all resources.
Query Parameters
- office: (string)
Filter by office id.
- name: (string)
The exact name of the item you are looking for. Also, you can use this property to perform a wildcard match.
- type: (string)
One of the possible resource types.
- availableFrom: (date)
Query available resources from the specified date.
- availableTo: (date)
Query available resources to the specified date.
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "56535da73492382393f68e2b",
"name": "1-1",
"room": "5804f7935a8e12f92e8694f3",
"targetPlan": null,
"office": "57575786849afaf41b0294f9",
"type": "desk"
}]
/resources/{resourceId}
Retrieve individual resource.
Get the resource with id = {resourceId}
URI Parameters
- resourceId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
{
"_id": "56535da73492382393f68e2b",
"name": "1-1",
"room": "5804f7935a8e12f92e8694f3",
"targetPlan": null,
"office": "57575786849afaf41b0294f9",
"type": "desk_tr"
}
HTTP status code 404
Body
Type: application/json
Example:
{ "message": "Resource not found" }
Resource Types
Resource types can be extended and modified, but the default ones are:
- meeting rooms - meeting_room
- private offices - team_room
- private office desk - desk_tr
- dedicated desks - desk
- hot desks - hotdesk
Target types (Floorplan object type):
- desk
- zone
Booking modes:
- time
- date
Check-in modes:
- half_day
- day
You can retrieve a list of all your resource types.
Resource Types
Property | Type | Required | Description |
---|---|---|---|
title | string | true | The name of the resource type |
type | string | true | A unique key that links the resource type to a zone type. |
target | enum | true | The floorplan object type of the resource. One of types listed above. |
bookingMode | enum | false | Booking mode of the resource type. One of types listed above. |
checkinMode | enum | false | The check-in mode of the resource type (applicable to hotdesk type only). One of types listed above. |
icon | string | false | An icon to represent the resource type on the portal. |
color | string | false | A color that color codes the resource type on the floorplan. |
canBook | boolean | false | If true, the resource type is available for booking on a calendar. |
canAssign | boolean | false | If true, memberships is assignable to the resource type. |
primary | boolean | false | If true, the resource occupancy can be tracked in the Occupancy section of the Dashboard. |
hierarchicalResourcesEnabled | boolean | false | If true, the resource type allows parent/child relationship (applicable to meeting_room type only). |
/resource-types
Retrieve all resource types in the organization
Query Parameters
- type: (string)
One of the possible resource types.
- target: (string)
One of the possible resource target types.
HTTP status code 200
Body
Type: application/json
Example:
[
{
"bookingMode": "time",
"checkinMode": "day",
"hierarchicalResourcesEnabled": false,
"_id": "5e37ebbc5bc88201b7aa5145",
"icon": "fa-building",
"title": "Private office",
"color": "#b3ae7a",
"type": "team_room",
"target": "zone",
"isSystem": true,
"canBook": false,
"canAssign": true,
"primary": true
},
{
"bookingMode": "time",
"checkinMode": "day",
"hierarchicalResourcesEnabled": false,
"_id": "5e37ebbc5bc88201b7aa5148",
"icon": "fa-desktop",
"title": "Dedicated desk",
"color": "#9898b7",
"type": "desk",
"target": "desk",
"isSystem": true,
"canBook": false,
"canAssign": true,
"primary": true
},
{
"bookingMode": "time",
"checkinMode": "day",
"hierarchicalResourcesEnabled": false,
"_id": "5e37ebbc5bc88201b7aa5146",
"icon": "fa-calendar-check-o",
"title": "Meeting room",
"color": "#b3ae7a",
"canBook": true,
"type": "meeting_room",
"target": "zone",
"isSystem": true
},
{
"bookingMode": "time",
"checkinMode": "day",
"hierarchicalResourcesEnabled": false,
"_id": "5e37ebbc5bc88201b7aa5147",
"icon": "fa-sitemap",
"title": "Office desk",
"color": "#9898b7",
"type": "desk_tr",
"target": "desk",
"isSystem": true,
"canAssign": true
},
{
"bookingMode": "time",
"checkinMode": "day",
"hierarchicalResourcesEnabled": false,
"_id": "5e37ebbc5bc88201b7aa514a",
"icon": "fa-user-times",
"title": "Not available",
"color": "#FFFFFF",
"type": "desk_na",
"target": "desk",
"isSystem": true
},
{
"bookingMode": "time",
"checkinMode": "day",
"hierarchicalResourcesEnabled": false,
"_id": "5e37ebbc5bc88201b7aa5149",
"icon": "fa-laptop",
"title": "Hotdesk",
"color": "#f58870",
"type": "hotdesk",
"target": "desk",
"canBook": true,
"isSystem": true,
"primary": true
},
{
"bookingMode": "date",
"checkinMode": "day",
"hierarchicalResourcesEnabled": false,
"_id": "5e3aa99550b9e80221b834d3",
"target": "zone",
"title": "Parking Lot",
"icon": "fa-car",
"type": "parking_lot",
"color": "#87f500",
"canAssign": true,
"primary": true,
"canBook": true
},
{
"bookingMode": "time",
"checkinMode": "day",
"hierarchicalResourcesEnabled": false,
"_id": "5e9589314ec9ba111d42a569",
"target": "zone",
"title": "Meetings",
"icon": "fa-meetup",
"type": "meetings",
"canBook": true,
"canAssign": true,
"primary": true,
"color": "#db1818"
}
]
Contracts
Contract objects allow you to associate contract data to your companies. Contracts can contain terms for both Services and Private offices. The API allows you to create, delete, and update contracts. You can retrieve individual contracts as well as a list of all your contracts.
/contracts
Retrieve all contracts.
Query Parameters
- team: (string)
Filter by team id
- office: (string)
Filter by office id.
HTTP status code 200
Body
Type: application/json
Example:
[{
"resourcesTotal": 156.25,
"servicesTotal": 3004.7999999999997,
"baseTotal": 3161.0499999999997,
"total": 3793.2599999999993,
"_id": "5a707fce37daead01decb3f1",
"startDate": "2018-01-30T00:00:00.000Z",
"endDate": "2019-01-29T00:00:00.000Z",
"office": "5a13f0437bab08ac23b269d4",
"number": "CON-15",
"team": "5a13f0437bab08ac23b269d5",
"member": "5a13f0437bab08ac23b269d6",
"documentType": "licenseAgreement",
"organization": "5a13f0437bab08ac23b269d3",
"createdAt": "2018-01-30T14:23:10.846Z",
"createdBy": "5a13ef3424a4d396232bb4eb",
"plans": [{
"id": "5a55ca64e29b41890a402653",
"listPrice": 250,
"price": 250,
"deposit": 200,
"count": 1,
"steps": [{
"startDate": "2018-01-30T00:00:00.000Z",
"endDate": "2019-01-29T00:00:00.000Z",
"price": 250
}]
}],
"resources": [{
"id": "5a5e0fba0a99dbc227a96933",
"size": 13,
"listPrice": 13,
"deposit": 0,
"steps": [{
"startDate": "2018-01-30T00:00:00.000Z",
"endDate": "2019-01-29T00:00:00.000Z",
"price": 13
}]
}],
"notice": {
"months": 3
},
"type": "new",
"status": "not_signed"
}]
Add new contracts
Body
Type: application/json
Example:
{
"startDate": "2018-01-30T00:00:00.000Z",
"endDate": "2019-01-29T00:00:00.000Z",
"office": "5a13f0437bab08ac23b269d4",
"notice": {
"months": 3
},
"type": "new",
"resources": [{
"steps": [{
"startDate": "2018-01-30T00:00:00.000Z",
"endDate": "2019-01-29T00:00:00.000Z",
"price": 13
}],
"id": "5a5e0fba0a99dbc227a96933",
"size": 13,
"listPrice": 13,
"deposit": 0
}],
"plans": [{
"steps": [{
"startDate": "2018-01-30T00:00:00.000Z",
"endDate": "2019-01-29T00:00:00.000Z",
"price": 250
}],
"id": "5a55ca64e29b41890a402653",
"listPrice": 250,
"price": 250,
"deposit": 200,
"count": 1
}],
"properties": {},
"number": "CON-15",
"team": "5a13f0437bab08ac23b269d5",
"member": "5a13f0437bab08ac23b269d6",
"status": "not_signed"
}
HTTP status code 200
Body
Type: application/json
Example:
[{
"resourcesTotal": 156.25,
"servicesTotal": 3004.7999999999997,
"baseTotal": 3161.0499999999997,
"total": 3793.2599999999993,
"_id": "5a707fce37daead01decb3f1",
"startDate": "2018-01-30T00:00:00.000Z",
"endDate": "2019-01-29T00:00:00.000Z",
"office": "5a13f0437bab08ac23b269d4",
"number": "CON-15",
"team": "5a13f0437bab08ac23b269d5",
"member": "5a13f0437bab08ac23b269d6",
"documentType": "licenseAgreement",
"organization": "5a13f0437bab08ac23b269d3",
"createdAt": "2018-01-30T14:23:10.846Z",
"createdBy": "5a13ef3424a4d396232bb4eb",
"plans": [{
"id": "5a55ca64e29b41890a402653",
"listPrice": 250,
"price": 250,
"deposit": 200,
"count": 1,
"steps": [{
"startDate": "2018-01-30T00:00:00.000Z",
"endDate": "2019-01-29T00:00:00.000Z",
"price": 250
}]
}],
"resources": [{
"id": "5a5e0fba0a99dbc227a96933",
"size": 13,
"listPrice": 13,
"deposit": 0,
"steps": [{
"startDate": "2018-01-30T00:00:00.000Z",
"endDate": "2019-01-29T00:00:00.000Z",
"price": 13
}]
}],
"notice": {
"months": 3
},
"type": "new",
"status": "not_signed"
}]
You can delete contracts by passing array of IDs to be deleted as request body.
Body
Type: application/json
Example:
["58c68b2c302d898ad348fde3"]
HTTP status code 200
Body
Type: application/json
Example:
[{
"resourcesTotal": 156.25,
"servicesTotal": 3004.7999999999997,
"baseTotal": 3161.0499999999997,
"total": 3793.2599999999993,
"_id": "5a707fce37daead01decb3f1",
"startDate": "2018-01-30T00:00:00.000Z",
"endDate": "2019-01-29T00:00:00.000Z",
"office": "5a13f0437bab08ac23b269d4",
"number": "CON-15",
"team": "5a13f0437bab08ac23b269d5",
"member": "5a13f0437bab08ac23b269d6",
"documentType": "licenseAgreement",
"organization": "5a13f0437bab08ac23b269d3",
"createdAt": "2018-01-30T14:23:10.846Z",
"createdBy": "5a13ef3424a4d396232bb4eb",
"plans": [{
"id": "5a55ca64e29b41890a402653",
"listPrice": 250,
"price": 250,
"deposit": 200,
"count": 1,
"steps": [{
"startDate": "2018-01-30T00:00:00.000Z",
"endDate": "2019-01-29T00:00:00.000Z",
"price": 250
}]
}],
"resources": [{
"id": "5a5e0fba0a99dbc227a96933",
"size": 13,
"listPrice": 13,
"deposit": 0,
"steps": [{
"startDate": "2018-01-30T00:00:00.000Z",
"endDate": "2019-01-29T00:00:00.000Z",
"price": 13
}]
}],
"notice": {
"months": 3
},
"type": "new",
"status": "not_signed"
}]
/contracts/{contractId}
Get the contract with id = {contractId}
URI Parameters
- contractId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
{
"_id": "58c45aa1261dd205c275bc78",
"team": "58736fbda872255b08624f40",
"office": "5873945c3ad3dd0e101ffa04",
"startDate": "2017-03-11T00:00:00.000Z",
"renewalDate": "2018-03-11T00:00:00.000Z",
"signDate": "2017-02-15T00:00:00.000Z",
"signedBy": {
"_id": "57fd4e3f910736be4786c1d7",
"displayName": "John Doe"
},
"number": "CON-MNCHSTR-1",
"plans": [],
"resources": [{
"id": "5873960d3ad3dd0e101ffa3b",
"listPrice": 500,
"price": 300
}],
"notice": {
"months": 3
},
"stage": "active",
"status": "signed"
}
HTTP status code 404
Body
Type: application/json
Example:
{ "message": "Contract could not be found" }
/contracts/{contractId}/sign
Sign the contract with id = {contractId}
. Contracts can be signed only if they are approved and they are not already signed.
URI Parameters
- contractId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
{
"_id": "58c45aa1261dd205c275bc78",
"team": "58736fbda872255b08624f40",
"office": "5873945c3ad3dd0e101ffa04",
"startDate": "2017-03-11T00:00:00.000Z",
"renewalDate": "2018-03-11T00:00:00.000Z",
"signDate": "2017-02-15T00:00:00.000Z",
"signedBy": {
"_id": "57fd4e3f910736be4786c1d7",
"displayName": "John Doe"
},
"number": "CON-MNCHSTR-1",
"plans": [],
"resources": [{
"id": "5873960d3ad3dd0e101ffa3b",
"listPrice": 500,
"price": 300
}],
"notice": {
"months": 3
},
"stage": "active",
"status": "signed"
}
HTTP status code 400
Body
Type: application/json
Example:
{ "message": "The contract has not been approved yet." }
/contracts/{contractId}/terminate
Terminate the contract with id = {contractId}
. Contracts can be terminated only if they are already signed and they are not terminated or expired already.
URI Parameters
- contractId: required (string)
Body
Type: application/json
Example:
{
"terminationInfo": {
"date": "2018-10-01T00:00:00.000Z",
"reasonKey": "system_office_move_client_request_move"
}
}
HTTP status code 200
Body
Type: application/json
Example:
{
"_id": "58c45aa1261dd205c275bc78",
"team": "58736fbda872255b08624f40",
"office": "5873945c3ad3dd0e101ffa04",
"startDate": "2017-03-11T00:00:00.000Z",
"renewalDate": "2018-03-11T00:00:00.000Z",
"signDate": "2017-02-15T00:00:00.000Z",
"signedBy": {
"_id": "57fd4e3f910736be4786c1d7",
"displayName": "John Doe"
},
"termination": {
"date": "2018-10-01T00:00:00.000Z",
"reasonKey": "system_office_move_client_request_move"
},
"number": "CON-MNCHSTR-1",
"plans": [],
"resources": [{
"id": "5873960d3ad3dd0e101ffa3b",
"listPrice": 500,
"price": 300
}],
"notice": {
"months": 3
},
"stage": "terminated",
"status": "terminated"
}
HTTP status code 400
Body
Type: application/json
Example:
{ "message": "The contract has not been approved yet." }
Credits
Credits are a way to book a paid meeting room, without paying. There are two types of measuring credits:
- Hourly-based credits - these are credits per hour, which you can use for a set of meeting rooms
- Coin-based credits - these are virtual coin credits, which can be spent at some rate when booking meeting rooms
The credits are with different interval lengths.
- Once - is amount of credits given at some point in time. They are valid forever, until they're fully utilized or removed.
- Monthly - is amount of credits given at the beginning of every month. After the end of the month, these are not carried over.
The API allows you to get the balance of your coin-based credits.
/credit-accounts
Get the coin credit balance for a team/member in a specific month
For example /credit-accounts/stats?team={team_id}&month=2018-07-01T00:00:00.000Z
Returns in summary the balance, monthly and once balances of the coins.
Query Parameters
- month: (date)
Filter by month
- member: (string)
Filter by member id
- team: (string)
Filter by team id
HTTP status code 200
Body
Type: application/json
Example:
[{
"creditAccount": "5b55fa531841603aa2aea2ca",
"type": "value",
"rates": [],
"team": "5b55fa3b1841603aa2aea2c3",
"summary": {
"total": 136,
"remaining": 90,
"used": 53.5
},
"monthly": {
"total": 120,
"remaining": 74,
"used": 53.5
},
"once": {
"total": 16,
"remaining": 16,
"used": 0
}
}]
Leads
Lead objects allow you to keep your enquiries and potential customers data in single place, add their contact details, add data about the services they are interested at, the deal size and the status of the conversation. The API allows you to create, delete, and update your lead records. You can retrieve individual lead as well as a list of all your leads or filter them by status.
/leads
Retrieve all leads.
Query Parameters
- contact: (string)
Filter by member id.
- status: (one of open, won, lost)
Filter by lead status. Convenient if you need list all open leads.
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "57a1a8d8c334490c73b2856f",
"contact": "565748274a955c790d808c77",
"status": "open",
"dealSize": 1200
}]
Add new lead. You can also add the contact inline.
Body
Type: application/json
Example:
[{
"contact": {
"name": "John Doe",
"email": "john@doe.com",
"phone": "+442130172",
"team": "565748274a955c790d808c77"
},
"status": "open",
"dealSize": 1200,
"name" : "Deal name"
}]
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "57a1a8d8c334490c73b2856f",
"contact": "565748274a955c790d808c77",
"status": "open",
"dealSize": 1200
}]
You can delete leads by passing array of IDs to be deleted as request body.
Body
Type: application/json
Example:
["57a1a8d8c334490c73b2856f"]
Posts
Post objects describe community posts added to OfficeRnD.
You can retrieve individual post as well as query all the posts by location, company or member.
Here is the list of the post object properties:
Post
Property | Type | Required | Description |
---|---|---|---|
type | string | true | The type of the post - currently the only supported type is info. |
description | string | true | The content of the post. |
locations | [id] | true | List of locations for which this post is relevant. If you want to be for all locations - just pass an empty array [] |
url | string | false | The URL for the image included in the post headline. |
team | id | false | A reference to the company that creates the post. |
member | id | false | A reference to the member that creates the post. |
/posts
Retrieve all posts
HTTP status code 200
Body
Type: application/json
Example:
[{
"type": "info",
"locations": [],
"_id": "5c51b3781eec2500105a8f54",
"member": {
"_id": "5c51b31eb6f966000ea2db45",
"name": "permissionsremain@milko.it",
"image": null
},
"title": "HtG perms",
"organization": "5a66081c2d528b14000e3582",
"createdAt": "2019-01-30T14:23:52.295Z",
"createdBy": "5c51b3231eec2500105a8c3e"
}]
Add new post
Body
Type: application/json
Example:
{
"type":"info",
"locations":[],
"title":"New coffee in the bar",
"url":"https://url-to-image",
"description":"There is a new coffe in the bar",
"image":"//dzrjcxtasfoip.cloudfront.net/user-resources/organization/qa23-1553507628409.jpeg"
}
HTTP status code 200
Body
Type: application/json
Example:
[{
"type":"info",
"locations":[],
"_id":"5c98a531463ed0000fc2d59b",
"member":"5c5c09e55400f04503c63688",
"title":"yo 1",
"url":"https://url-to-image",
"description":"content 1",
"image":"//dzrjcxtasfoip.cloudfront.net/user-resources/organization/qa23-1553507628409.jpeg","organization":"5a66081c2d528b14000e3582",
"createdAt":"2019-03-25T09:53:53.138Z",
"createdBy":"5a13ef3424a4d396232bb4eb"
}]
You can delete post by passing the ID of the POST in the request url
Body
Type: application/json
Example:
["5df567b8ddf8bf1600ce1f59"]
/posts/{eventId}
Gets a single post by postId.
URI Parameters
- eventId: required (string)
The Id of the corresponding post
HTTP status code 200
Body
Type: application/json
Example:
{
"type":"info",
"locations":[],
"_id":"5c51b3781eec2500105a8f54",
"member":"5c51b31eb6f966000ea2db45",
"title":"HtG perms",
"organization":"5a66081c2d528b14000e3582",
"createdAt":"2019-01-30T14:23:52.295Z",
"createdBy":"5c51b3231eec2500105a8c3e"
}
Events
Event objects describe community events added to OfficeRnD.
You can retrieve individual event as well as query all the events by location, company or member.
Here is the list of the event object properties:
Event
Property | Type | Required | Description |
---|---|---|---|
start | date | true | The start date and time of the event. Must be a ISO-formatted in UTC. |
end | date | true | The end date and time of the event. Must be a ISO-formatted in UTC. |
office | id | true | A reference to the location the invoiced is issued by. |
team | id | false | A reference to the company that organizes the event. |
member | id | false | A reference to the member that organizes the event. |
title | string | false | A brief title of the event. |
description | string | false | A detailed description of the event. |
where | string | false | Additional information for the location of the event. |
timezone | string | false | The timezone of the event |
image | string | false | A url to an image for the event. |
limit | number | false | The number of participants that this event will host. |
/events
Retrieve all events
Query Parameters
- start.$gt: (date)
All items starting after the pointed date
- start.$lt: (date)
All items starting before the pointed date
- end.$gt: (date)
All items ending after the pointed date
- end.$lt: (date)
All items ending before the pointed date
- $populate: (string)
Comma-separated list of all properties to be populated. Only id references can be populated. Refer to Populating queries section for more information.
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "5ab7943b714ff61400f79d43",
"start": "2018-04-06T16:00:00.000Z",
"end": "2018-04-06T17:00:00.000Z",
"timezone": "Europe/London",
"office": "5ad214b8acc8bf1600ce1f59",
"title": "Free Lunch Tomorrow & Board Games",
"description": "Lunch & Board Games",
"where": "Meeting Room 1",
"limit": 10,
"team": "5ab214c5acc8bf1600ce1f5a",
"member": {
"_id": "5ab255b8acc8bf1600ce1f5b",
"name": "John",
"image": "{url}"
},
"status": "past",
"reservations": {
"yes": 0,
"interested": 0,
"no": 0
}
}]
Add new events
Body
Type: application/json
Example:
[{
"start": "2018-04-06T16:00:00.000Z",
"end": "2018-04-06T17:00:00.000Z",
"timezone": "Europe/London",
"office": "5ad243b8acc8bf1600ce1f59",
"title": "Free Lunch Tomorrow & Board Games",
"description": "Lunch & Board Games",
"where": "Meeting Room 1",
"limit": 10,
"team": "5ab214c5acc8bf1444ce1f5a",
"member": "5ab255b8acc8bf1444ce1f5b"
}]
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "5df567b8ddf8bf1600ce1f59",
"start": "2018-04-06T16:00:00.000Z",
"end": "2018-04-06T17:00:00.000Z",
"timezone": "Europe/London",
"office": "5ad243b8acc8bf1600ce1f59",
"title": "Free Lunch Tomorrow & Board Games",
"description": "Lunch & Board Games",
"where": "Meeting Room 1",
"limit": 10,
"team": "5ab214c5acc8bf1444ce1f5a",
"member": "5ab255b8acc8bf1444ce1f5b"
}]
You can delete events by passing array of IDs to be deleted as request body.
Body
Type: application/json
Example:
["5df567b8ddf8bf1600ce1f59"]
/events/{eventId}
Gets a single event by eventId.
URI Parameters
- eventId: required (string)
The Id of the corresponding event
Query Parameters
- $populate: (string)
Comma-separated list of all properties to be populated. Only id references can be populated. Refer to Populating queries section for more information.
HTTP status code 200
Body
Type: application/json
Example:
{
"_id": "5ab7943b714ff61400f79d43",
"start": "2018-04-06T16:00:00.000Z",
"end": "2018-04-06T17:00:00.000Z",
"timezone": "Europe/Sofia",
"office": "5ad214b8acc8bf1600ce1f59",
"title": "Free Lunch Tomorrow & Board Games",
"description": "Lunch & Board Games",
"where": "Meeting Room 1",
"limit": 10,
"team": "5ab214c5acc8bf1600ce1f5a",
"member": {
"_id": "5ab255b8acc8bf1600ce1f5b",
"name": "John",
"image": "{url}"
},
"status": "past",
"reservations": {
"yes": 0,
"interested": 0,
"no": 0
}
}
Update the event with id = {eventId}
URI Parameters
- eventId: required (string)
The Id of the corresponding event
Body
Type: application/json
Example:
{
"title": "New Event title",
"where": "The lobby bar"
}
HTTP status code 200
Body
Type: application/json
Example:
{
"_id": "5ab7943b714ff61400f79d43",
"start": "2018-04-06T16:00:00.000Z",
"end": "2018-04-06T17:00:00.000Z",
"timezone": "Europe/Sofia",
"office": "5ad214b8acc8bf1600ce1f59",
"title": "New Event title",
"description": "Lunch & Board Games",
"where": "The lobby bar",
"limit": 10,
"team": "5ab214c5acc8bf1600ce1f5a",
"member": {
"_id": "5ab255b8acc8bf1600ce1f5b",
"name": "John",
"image": "{url}"
},
"status": "past",
"reservations": {
"yes": 0,
"interested": 0,
"no": 0
}
}
Visits
Collection of the visits in the organization
/visits
Lists visits in the organization. You can add "$populate=visitor" in the query string to populate visitor data along with the visit data.
Query Parameters
- start.$gt: (date)
All items starting after the pointed date
- start.$lt: (date)
All items starting before the pointed date
- end.$gt: (date)
All items ending after the pointed date
- end.$lt: (date)
All items ending before the pointed date
- $populate: (string)
Comma-separated list of all properties to be populated. Only id references can be populated. Refer to Populating queries section for more information.
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "57a1a8d8c334490c73b2856f",
"office": "565748274a955c790d808c77",
"visitor": {
"_id": "57a1a88ac334490c73b2856d",
"email": "miron@officernd.com",
"name": "Miroslav Nedialkov",
"team": "56535edb3492382393f68e5d",
"actualImage": "//www.gravatar.com/avatar/d3c6debc20d468778003389094eaeb18?s=263&d=mm"
},
"team": "56535edb3492382393f68e5d",
"member": "5798cf377200d6a001f84b3d",
"end": "2016-08-03T11:18:22.732Z",
"start": "2016-08-03T10:18:22.732Z"
}]
/visits/{visitId}
Gets a single visit by visitId. You can add "$populate=visitor" in the query string to populate visitor data along with the visit data.
URI Parameters
- visitId: required (string)
The Id of the corresponding visit
Query Parameters
- $populate: (string)
Comma-separated list of all properties to be populated. Only id references can be populated. Refer to Populating queries section for more information.
HTTP status code 200
Body
Type: application/json
Example:
{
"_id": "57a1a8d8c334490c73b2856f",
"office": "565748274a955c790d808c77",
"visitor": {
"_id": "57a1a88ac334490c73b2856d",
"email": "miron@officernd.com",
"name": "Miroslav Nedialkov",
"team": "56535edb3492382393f68e5d",
"actualImage": "//www.gravatar.com/avatar/d3c6debc20d468778003389094eaeb18?s=263&d=mm"
},
"member": "5798cf377200d6a001f84b3d",
"team": "56535edb3492382393f68e5d",
"end": "2016-08-03T11:18:22.732Z",
"start": "2016-08-03T10:18:22.732Z"
}
Visitors
You can retrieve a list of all organization visitors. You can also add new visitor to your organization.
Visitors
Property | Type | Required | Description |
---|---|---|---|
name | string | true | The full name of the visitor |
string | true | The email address of the visitor | |
description | string | false | Description of the visitor |
phone | string | false | The phone number of the visitor |
/visitors
Retrieve all visitors in the organization
Query Parameters
- email: (string)
Find visitor by an email.
HTTP status code 200
Body
Type: application/json
Example:
[
{
"_id": "5f201124480a2244066a51b7",
"team": "5e37ebbc5bc88201b7aa510f",
"member": "5e41665d4f686500100f0d8a",
"name": "Test Visitor 1",
"email": "visitor1@test.com"
},
{
"_id": "5f20114d480a2244066a51ba",
"team": "5e37ebbc5bc88201b7aa510f",
"member": "5e41665d4f686500100f0d8a",
"name": "Test Visitor 2",
"email": "visitor2@test.com"
}
]
Add a new visitor.
Body
Type: application/json
Example:
[{
"name": "Test Visitor",
"email": "testvisitor@test.com",
"phone": "+442130172"
}]
HTTP status code 200
Body
Type: application/json
Example:
[{
"name": "Test Visitor",
"email": "testvisitor@test.com",
"phone": "+442130172"
}]
Payments
Payment objects describe invoices, credit notes (credit memos), charges and credit allocations. They might contain information about any external links the invoice has.
You can retrieve individual invoices/credit notes as well as query all the items.
Here is the list of all properties the payment object has:
Payment
Property | Type | Required | Description |
---|---|---|---|
number | string | true | The document number for the invoice / credit memo. |
documentType | enum | false | The type of the document. It could be either invoice or creditNote. If ommited, invoice is assumed. |
date | date | true | The issue date of the document. |
dueDate | date | true | The due date of the document. |
team | id | false | A reference to the company the invoice is issued for. |
member | id | false | A reference to the member or individual the invoice is issued for. |
office | id | true | A reference to the location the invoiced is issued by. |
reference | string | false | Additional data describing the invoice. It could be any string value. |
taxType | enum | true | The type of the tax calculation for the invoice. It could be excluded, included or noTax. |
allocations | array | readonly | A list of credit allocations. Each allocation has amount and target. |
invoiceLines | array | readonly | A list of invoice line items. |
vatAmounts | array | readonly | A list of invoice vat amounts. |
subtotal | number | readonly | The total of the invoice / credit note before tax. |
amount | number | readonly | The total of the invoice / credit note after tax and discount. |
payableAmount | number | readonly | Total amount due for this invoice. Voided and paid invoices has payableAmount of 0. |
createdAt | date | readonly | The date when the payment has been created at. |
createdBy | id | readonly | The user that created the payment. |
modifiedAt | date | readonly | The date when the payment has been modified for the last time. Before the first modification, this field equals to the createdAt field. Note that adding/removing charges and credit allocations are considered modifications to both the invoice and the credit note/overpayment. |
modifiedBy | id | readonly | The user that did the last modification to the payment. Before the first modification, this field equals to the createdBy field. |
Credit Allocation
Property | Type | Required | Description |
---|---|---|---|
target | id | true | If payment is an invoice, a reference to the credit note, sourcing the creidt. If payment is a credit note, a reference to the invoice the credit is allocated to. |
amount | number | true | The allocated amount. |
Line Item
Property | Type | Required | Description |
---|---|---|---|
description | string | true | Description of the line item. |
account | id | true | A reference to the account the line item is allocated to. |
vatPercent | number | true | The tax percent applied to the line item. |
vatAmount | number | true | The tax amount applied to the line item. |
quantity | number | true | The quantity of items described by the line item. |
unitPrice | number | true | The unit price as described in the system. |
discountAmount | number | true | The calculated discount amount based on the discount set in the invoice. |
baseUnitPrice | number | true | The calculated unit price without tax. |
baseTotal | number | true | The total wihtout tax. |
total | number | true | The total for the line item (including tax). |
Vat Amount
Property | Type | Required | Description |
---|---|---|---|
percent | number | true | The tax percent for the group. |
total | number | true | The tax total for the group. |
taxRate | id | true | A reference to the taxRate the group is for. |
/payments
Retrieve all payment objects (invoices / credit notes)
Query Parameters
- modifiedAt.$gt: (date)
All items created or last modified after the pointed date
- modifiedAt.$lt: (date)
All items created or last modified before the pointed date
- createdAt.$gt: (date)
All items created after the pointed date
- createdAt.$lt: (date)
All items created before the pointed date
- member: (string)
Filter by member id
- team: (string)
Filter by team id
- office: (string)
Filter by office id.
- $populate: (string)
Comma-separated list of all properties to be populated. Only id references can be populated. Refer to Populating queries section for more information.
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "59a43cfbefd2661700e9f744",
"number": "INV-14",
"team": "565362e03492382393f68e71",
"member": null,
"office": "565748274a955c790d808c77",
"date": "2017-08-28T00:00:00.000Z",
"dueDate": "2017-09-01T00:00:00.000Z",
"reference": "September Invoice",
"taxType": "excluded",
"allocations": [{
"amount": 100,
"target": "59a43d1aefd2661700e9f76c"
}],
"subtotal": 250,
"discountAmount": 0,
"vatAmounts": [{
"percent": 20,
"taxRate": "57d6b754871bb8bdb46a7e67",
"total": 50
}],
"amount": 300,
"paidAmount": 200,
"allocatedAmount": 100,
"pendingAmount": 0,
"payableAmount": 0,
"status": "paid",
"invoiceLines": [{
"description": "Dedicated Desk, $250.00, Sep 1, 2017 - Sep 30, 2017",
"discount": 0,
"account": "57b1d525462a54e463af70ec",
"vatPercent": 20,
"taxRate": "57d6b754871bb8bdb46a7e67",
"plan": "56535f483492382393f68e5e",
"unitPrice": 250,
"baseUnitPrice": 250,
"quantity": 1,
"discountAmount": 0,
"subtotal": 250,
"baseTotal": 250,
"vatAmount": 50,
"total": 300
}]
}]
/payments/{paymentId}
Retrieve an individual invoice or credit note.
Get the invoice / credit note with id = {paymentId}
URI Parameters
- paymentId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
{
"_id": "59a43d1aefd2661700e9f76c",
"number": "INV-15",
"team": "565362e03492382393f68e71",
"member": null,
"office": "565748274a955c790d808c77",
"date": "2017-08-28T00:00:00.000Z",
"documentType": "creditNote",
"taxType": "excluded",
"allocations": [{
"amount": 100,
"target": "59a43cfbefd2661700e9f744"
}],
"subtotal": 83.33,
"vatAmounts": [{
"percent": 20,
"taxRate": "57d6b754871bb8bdb46a7e67",
"total": 16.67
}],
"amount": 100,
"discountAmount": 0,
"discount": 0,
"paidAmount": 0,
"allocatedAmount": 100,
"pendingAmount": 0,
"payableAmount": 0,
"status": "paid",
"invoiceLines": [{
"description": "Dedicated Desk, $250.00, Sep 1, 2017 - Sep 30, 2017",
"account": "57b1d525462a54e463af70ec",
"vatPercent": 20,
"taxRate": "57d6b754871bb8bdb46a7e67",
"plan": "56535f483492382393f68e5e",
"quantity": 0.3333,
"unitPrice": 250,
"discountAmount": 0,
"baseUnitPrice": 250,
"vatAmount": 16.67,
"subtotal": 83.33,
"baseTotal": 83.33,
"total": 100
}]
}
HTTP status code 404
Body
Type: application/json
Example:
{ "message": "Payment not found" }
/payments/{paymentId}/documents
All document information for a specified invoice/credit note.
Get all documents metadata for a specific invoice. Each item in the response contains information about the document name, template and url.
URI Parameters
- paymentId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "5a6264331746d306534acf01",
"name": "INV-201-Invoice Primary",
"url": "https://app.officernd.com/download/eyJhbGciOiJIUzI1NiIsII6IkpXVCJ9.eyJwYXltZW50MzE3NDZkMzA2NTM0YWNmMDEiLCJ0ZW1wbGF0ZU5hbWUiOiJpbnZvaWNlX3ByaW1hcnkiLCJvcmdhbml6YXRpb24iOiI1ODgxZTU1NGY5MGFhOTI4NzU3YmM1NzEiLCJpYXQiOjE1NDI4OTYxMzQ1MzczNH0.YWFCz8na9Tg0TQi1NmYUmBURpQMviAA",
"templateName": "Invoice Primary"
}]
/payments/{paymentId}/charges
All charges for a specified invoice/credit note. Please check the Charges endpoint for more details.
Get all charges for a specific invoice.
URI Parameters
- paymentId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id": "59a43c4d99852e1500f519d2",
"date": "2017-08-28T15:52:46.890Z",
"reference": "**** **** **** 4242 (Visa)",
"account": "Stripe Card",
"amount": 250,
"status": "success",
"source": "OfficeR&D",
"integration": {
"providerChargeId": "ch_1AvpXeEPo4o6pr4dASH0ElEF"
}
}]
Create a new charge for the specified invoice. All required fields must be specified.
URI Parameters
- paymentId: required (string)
Body
Type: application/json
Example:
{
"date": "2017-08-28T15:52:46.890Z",
"reference": "**** **** **** 4242 (Visa)",
"account": "Stripe Card",
"amount": 50,
"status": "success",
"source": "ERP"
}
HTTP status code 400
Body
Type: application/json
Example:
{
"name": "InvalidOperationError",
"message": "Invalid amount provided.",
"type": "Invalid operation"
}
/payments/{paymentId}/charges/{chargeId}
Manage individual charge.
Update the charge with id = {chargeId}
URI Parameters
- paymentId: required (string)
- chargeId: required (string)
Body
Type: application/json
Example:
{
"amount": 100,
"date": "2017-03-25T00:00:00.000Z",
"status": "fail"
}
HTTP status code 200
Body
Type: application/json
Example:
{
"source": "OfficeR&D",
"status": "fail",
"_id": "58bd5d3b34e0dc140091eba7",
"reference": "1234",
"account": "POS",
"date": "2017-03-25T00:00:00.000Z",
"amount": 100
}
Remove a charge with id = {chargeId}
URI Parameters
- paymentId: required (string)
- chargeId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
{
"source": "OfficeR&D",
"status": "fail",
"_id": "5a58b23b857eba94e49e1aaa",
"createdBy": "5a536980e8d4bb0919af56d3",
"createdAt": "2018-01-12T13:03:55.788Z",
"date": "2018-01-12T00:00:00.000Z",
"account": "Cash",
"amount": 10
}
Charges
Charge object describes a single payment associated with an invoice. Charges can be either created manually, or done via a payment gateway.
Here is the structure of the charge object
Charge
Property | Type | Required | Description |
---|---|---|---|
amount | number | true | The amount of the transaction. |
date | date | true | The exact date of the transaction. |
status | enum | true | The status of the transaction. One of the following success, pending, fail. |
reference | string | true | Human readable description of the transaction. |
source | string | false | The system the charge originated at. OfficeR&D by default |
account | string | false | The name of the account where money are depositet at. |
integration | object | false | Present if charge is collected via payment gateway. Its property providerChargeId is set to the gateway charge ID |
createdAt | date | readonly | The date when the charge has been created at. |
createdBy | id | readonly | The user that created the charge. |
modifiedAt | date | readonly | The date when the charge has been modified for the last time. Before the first modification, this field equals to the createdAt field. |
modifiedBy | id | readonly | The user that did the last modification to the charge. Before the first modification, this field equals to the createdBy field. |
payment | id | readonly | The invoice or credit note the charge is associated with. |
office | id | readonly | The location of the invoice or credit note the charge is assigned to. |
/charges
Retrieve all charge objects.
Query Parameters
- modifiedAt.$gt: (date)
All items created or last modified after the pointed date
- modifiedAt.$lt: (date)
All items created or last modified before the pointed date
- createdAt.$gt: (date)
All items created after the pointed date
- createdAt.$lt: (date)
All items created before the pointed date
- office: (string)
Filter charges by the location of their invoice.
- payment: (string)
Filter by parent invoice. Similar to /payments/{paymentId}/charges.
- status: (one of success, pending, fail)
Filter charges by their status. Usually you would need only the successful charges.
Network
API to help network modules integrate with OfficeRnD
/network/auth
Use this endpoint to authorize a new device for a member.
Body
Type: application/json
Schema:
{
"$schema": "http://json-schema.org/draft-03/schema",
"required": true,
"type": "object",
"properties": {
"username": {
"description": "the email address of the member",
"type": "string"
},
"password": {
"description": "The clear text password of the member",
"type": "string"
},
"macAddress": {
"description": "The mac address of the new device to be registered",
"type": "string"
},
"source": {
"description": "The name of the module that is requesting authorization",
"type": "string"
}
},
"required": ["username", "password", "macAddress"]
}
Example:
{
"username": "john.doe@gmail.com",
"password": "johndoe",
"macAddress": "01-23-45-67-89-ab",
"source": "OfficeRnD network module"
}
/network/checkin
Use this endpoint to checkin a member by their mac address.
Body
Type: application/json
Schema:
{
"$schema": "http://json-schema.org/draft-03/schema",
"required": true,
"type": "object",
"properties": {
"macAddress": {
"description": "The mac address of the device that logged to the network.",
"type": "string"
},
"office": {
"description": "The ID of the OfficeRnD location where the member devide logged in. If not specified no location is associated with the checkin.",
"type": "string"
},
"date": {
"description": "When the member device logged in. If not specified now is assumed.",
"type": "string"
}
},
"required": ["macAddress"]
}
Example:
{
"macAddress": "01-23-45-67-89-ab",
"office": "565748274a955c790d808c77"
}
/network/checkout
Use this endpoint to checkout a member by their mac address.
Body
Type: application/json
Schema:
{
"$schema": "http://json-schema.org/draft-03/schema",
"required": true,
"type": "object",
"properties": {
"macAddress": {
"description": "The mac address of the device that logged out from the network.",
"type": "string"
},
"date": {
"description": "When the member device logged out. If not specified now is assumed.",
"type": "string"
}
},
"required": ["macAddress"]
}
Example:
{
"macAddress": "01-23-45-67-89-ab"
}
Reports
/reports/occupancy
This set of reports provides aggregated data regarding the resources and members of the space. As this data is related to a specific time frame, these APIs allow quering data for different time ranges and divided by different time periods.
To set this time filter set the following query string parameters:
- from - start date of the period in ISO date format (i.e. 2017-10-01)
- to - end date of the period in ISO date format (i.e. 2017-11-31)
- interval - length of the time period the report is divided by (one of 'day', 'week', 'month')
/reports/occupancy/by-customer
Generates a report for each customer, containing information about the resource the customer is using in the space in the specified time period.
If not grouped, the report is an array of type By Customer Occupancy Stats. If grouped by office or floor, the result is an array of type Grouped By Customer Occupancy Stats.
By Customer Occupancy Stats
Property | Type | Description |
---|---|---|
team | object | The _id and name of the company the report entry is for (i.e. the group key). |
member | object | The _id and name of the company the report entry is for (i.e. the group key). |
stats | [object] | List of the report data divided by intervals. The value of this field is of type Customer Occupancy Stats. |
Customer Occupancy Stats
Property | Type | Description |
---|---|---|
date | date | The start date of the interval the stats are about (i.e. the subgroup key). |
total | object | The sum of all occupied resource used by the customer. The value of this field is of type Resource Type Stats. |
* (resource type) | object | The count of resources used by the customer. The value of this field is of type Resource Type Stats. The name of the field could be one of desk, hotdesk, team_room, or any other resource type. |
Resource Type Stats
Property | Type | Description |
---|---|---|
count | number | The count of resources of the specified type being used by the customer. |
units | number | The total units count of the specified type being used by the customer. This property is missing for single resources like desks and contain the count of the sub resources for resources like private offices. |
Grouped By Customer Occupancy Stats
Property | Type | Description |
---|---|---|
room | ID | Reference to the floorplan this group is for. Only present when grouped by room. |
office | ID | Reference to the location this group is for. Only present when grouped by office. |
byCustomer | [object] | The byCustomer report for the specified group. The value of this field is an array of type By Customer Occupancy Stats. |
Query Parameters
- $group: (string)
Property to group the list by (one of room, office). Default is null (i.e. no grouping)
- office: (string)
Filter by office id.
- from: (date)
Start of the period for reporting. Default is today.
- to: (date)
End of the period for reporting. Default is today.
- interval: (string)
Interval length to split the report (one of day, week, month). Default is day.
HTTP status code 200
Body
Type: application/json
Example:
[{
"team": {
"_id": "56535edb3492382393f68e5d",
"name": "OfficeRnD"
},
"stats": [{
"date": "2017-05-01T00:00:00.000Z",
"team_room": {
"count": 1,
"units": 4
},
"total": {
"count": 1,
"units": 4
}
}, {
"date": "2017-06-01T00:00:00.000Z",
"team_room": {
"count": 1,
"units": 4
},
"total": {
"count": 1,
"units": 4
}
}]
}, {
"team": {
"_id": "565362e03492382393f68e71",
"name": "PiLabs"
},
"stats": [{
"date": "2017-05-01T00:00:00.000Z",
"desk": {
"count": 1
},
"total": {
"count": 1,
"units": 1
}
}, {
"date": "2017-06-01T00:00:00.000Z",
"desk": {
"count": 1
},
"total": {
"count": 1,
"units": 1
}
}]
}]
/reports/occupancy/assignable-stats
Generates report containing occupancy statistics for all resource types, divided by periods as specified by the time period filter.
If not grouped, the report is an array of type Interval Occupancy Stats.
Interval Occupancy Stats
Property | Type | Description |
---|---|---|
date | date | The start date of the interval the stats are about (i.e. the group key). |
total | object | The sum/avg of occupancy stats for all resource types. The value of this field is of type Occupancy Stats. |
* (resource type) | object | The occpancy stats for each resource type. The value of this field is of type Extended Occupancy Stats. |
Occupancy Stats
Property | Type | Description |
---|---|---|
count | number | The count of the specified resource type. |
units | number | The total units count of the specified type. This property is missing for single resources like desks and contain the count of the sub resources for resources like private offices. |
occupied | number | The count of the occupied units of the specified resource type. For single resources this is the count of the resources occupied. |
occupancy | number | The occupancy rate for the specified resource type calculated based on units (i.e. occupied / units). For single resources this is the based on the resources count instead. |
Extended Occupancy Stats
All properties from Occupancy Stats plus
Property | Type | Description |
---|---|---|
statuses | [object] | Resources count grouped by their status. The value of each item is of type Status Group Stats. |
Status Group Stats
Property | Type | Description |
---|---|---|
status | string | The status of the group. |
count | number | The count of resources of the specified type and status for the specific date. |
units | number | The total units count of the specified type and status for the specific date. |
If the report is grouped (i.e. the $group query string parameter is passed), the report is an array of type Group Occupancy Stats.
Group Occupancy Stats
Property | Type | Description |
---|---|---|
room | ID | Reference to the floorplan this group is for. Only present when grouped by room. |
office | ID | Reference to the location this group is for. Only present when grouped by office. |
stats | object | The stats for the specified group. The value of this field is an array of type Interval Occupancy Stats. |
Query Parameters
- office: (string)
Filter by office id.
- $group: (string)
Property to group the list by (one of room, office). Default is null (i.e. no grouping)
- from: (date)
Start of the period for reporting. Default is today.
- to: (date)
End of the period for reporting. Default is today.
- interval: (string)
Interval length to split the report (one of day, week, month). Default is day.
HTTP status code 200
Body
Type: application/json
Example:
[{
"date": "2017-05-01T00:00:00.000Z",
"total": {
"count": 69,
"occupied": 22,
"occupancy": 0.29,
"units": 76
},
"desk": {
"statuses": [{
"status": "available",
"count": 16,
"units": 16
},
{
"status": "occupied",
"count": 18,
"units": 18
},
{
"status": "reserved",
"count": 6,
"units": 6
}
],
"count": 40,
"occupied": 18,
"occupancy": 0.45
},
"hotdesk": {
"statuses": [{
"status": "hotdesk",
"count": 16,
"units": 16
}],
"count": 16,
"occupied": 0,
"occupancy": 0
},
"desk_tr": {
"statuses": [{
"status": "available",
"count": 5,
"units": 5
},
{
"status": "occupied",
"count": 3,
"units": 3
},
{
"status": "reserved",
"count": 2,
"units": 2
}
],
"count": 10,
"occupied": 0,
"occupancy": 0
},
"team_room": {
"statuses": [{
"status": "available",
"count": 1,
"units": 5
},
{
"status": "occupied",
"count": 1,
"units": 4
},
{
"status": "reserved",
"count": 1,
"units": 1
}
],
"count": 3,
"occupied": 4,
"occupancy": 0.4,
"units": 10
}
}, {
"date": "2017-06-01T00:00:00.000Z",
"total": {
"count": 69,
"occupied": 23,
"occupancy": 0.3,
"units": 76
},
"desk": {
"statuses": [{
"status": "available",
"count": 17,
"units": 17
},
{
"status": "available_soon",
"count": 1,
"units": 1
},
{
"status": "occupied",
"count": 17,
"units": 17
},
{
"status": "reserved",
"count": 5,
"units": 5
}
],
"count": 40,
"occupied": 18,
"occupancy": 0.45
},
"hotdesk": {
"statuses": [{
"status": "hotdesk",
"count": 16,
"units": 16
}],
"count": 16,
"occupied": 0,
"occupancy": 0
},
"desk_tr": {
"statuses": [{
"status": "available",
"count": 5,
"units": 5
},
{
"status": "occupied",
"count": 4,
"units": 4
},
{
"status": "reserved",
"count": 1,
"units": 1
}
],
"count": 10,
"occupied": 0,
"occupancy": 0
},
"team_room": {
"statuses": [{
"status": "available",
"count": 1,
"units": 5
},
{
"status": "occupied",
"count": 2,
"units": 5
}
],
"count": 3,
"occupied": 5,
"occupancy": 0.5,
"units": 10
}
}, {
"date": "2017-07-01T00:00:00.000Z",
"total": {
"count": 69,
"occupied": 25,
"occupancy": 0.33,
"units": 76
},
"desk": {
"statuses": [{
"status": "available",
"count": 21,
"units": 21
},
{
"status": "occupied",
"count": 19,
"units": 19
}
],
"count": 40,
"occupied": 19,
"occupancy": 0.48
},
"hotdesk": {
"statuses": [{
"status": "hotdesk",
"count": 16,
"units": 16
}],
"count": 16,
"occupied": 0,
"occupancy": 0
},
"desk_tr": {
"statuses": [{
"status": "available",
"count": 5,
"units": 5
},
{
"status": "occupied",
"count": 5,
"units": 5
}
],
"count": 10,
"occupied": 1,
"occupancy": 0.1
},
"team_room": {
"statuses": [{
"status": "available",
"count": 1,
"units": 5
},
{
"status": "occupied",
"count": 2,
"units": 5
}
],
"count": 3,
"occupied": 5,
"occupancy": 0.5,
"units": 10
}
}, {
"date": "2017-08-01T00:00:00.000Z",
"total": {
"count": 69,
"occupied": 25,
"occupancy": 0.33,
"units": 76
},
"desk": {
"statuses": [{
"status": "available",
"count": 21,
"units": 21
},
{
"status": "occupied",
"count": 19,
"units": 19
}
],
"count": 40,
"occupied": 19,
"occupancy": 0.48
},
"hotdesk": {
"statuses": [{
"status": "hotdesk",
"count": 16,
"units": 16
}],
"count": 16,
"occupied": 0,
"occupancy": 0
},
"desk_tr": {
"statuses": [{
"status": "available",
"count": 5,
"units": 5
},
{
"status": "occupied",
"count": 5,
"units": 5
}
],
"count": 10,
"occupied": 1,
"occupancy": 0.1
},
"team_room": {
"statuses": [{
"status": "available",
"count": 1,
"units": 5
},
{
"status": "occupied",
"count": 2,
"units": 5
}
],
"count": 3,
"occupied": 5,
"occupancy": 0.5,
"units": 10
}
}, {
"date": "2017-09-01T00:00:00.000Z",
"total": {
"count": 69,
"occupied": 25,
"occupancy": 0.33,
"units": 76
},
"desk": {
"statuses": [{
"status": "available",
"count": 21,
"units": 21
},
{
"status": "occupied",
"count": 19,
"units": 19
}
],
"count": 40,
"occupied": 19,
"occupancy": 0.48
},
"hotdesk": {
"statuses": [{
"status": "hotdesk",
"count": 16,
"units": 16
}],
"count": 16,
"occupied": 0,
"occupancy": 0
},
"desk_tr": {
"statuses": [{
"status": "available",
"count": 5,
"units": 5
},
{
"status": "occupied",
"count": 5,
"units": 5
}
],
"count": 10,
"occupied": 1,
"occupancy": 0.1
},
"team_room": {
"statuses": [{
"status": "available",
"count": 1,
"units": 5
},
{
"status": "occupied",
"count": 2,
"units": 5
}
],
"count": 3,
"occupied": 5,
"occupancy": 0.5,
"units": 10
}
}, {
"date": "2017-10-01T00:00:00.000Z",
"total": {
"count": 69,
"occupied": 25,
"occupancy": 0.33,
"units": 76
},
"desk": {
"statuses": [{
"status": "available",
"count": 21,
"units": 21
},
{
"status": "occupied",
"count": 19,
"units": 19
}
],
"count": 40,
"occupied": 19,
"occupancy": 0.48
},
"hotdesk": {
"statuses": [{
"status": "hotdesk",
"count": 16,
"units": 16
}],
"count": 16,
"occupied": 0,
"occupancy": 0
},
"desk_tr": {
"statuses": [{
"status": "available",
"count": 5,
"units": 5
},
{
"status": "occupied",
"count": 5,
"units": 5
}
],
"count": 10,
"occupied": 1,
"occupancy": 0.1
},
"team_room": {
"statuses": [{
"status": "available",
"count": 1,
"units": 5
},
{
"status": "occupied",
"count": 2,
"units": 5
}
],
"count": 3,
"occupied": 5,
"occupancy": 0.5,
"units": 10
}
}]
Authentication
/auth/member
Authenticate member by given email. Returns the access token to login (impersonate) as a member.
The authentication token returned by this endpoint can be used to generate quick login links to the members portal. You just need to add access_token=<token> to the URL.
Example:
https://icowork.officernd.com/calendar?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjU4NDZmMjhhZmY1ZDg4MTI2YWJjZmMzZiIsImlhdCI6MTQ4MTExNjQwOCwiZXhwIjoxNDgxMjg5MjA4fQ.ElceBwjTRBhu669SWkn_Za8VKk-cwomzmbEsNywPt5A
Body
Type: application/json
Example:
{
"email": "info@officernd.com"
}
HTTP status code 200
Body
Type: application/json
Example:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjU4NDZmMjhhZmY1ZDg4MTI2YWJjZmMzZiIsImlhdCI6MTQ4MTExNjQwOCwiZXhwIjoxNDgxMjg5MjA4fQ.ElceBwjTRBhu669SWkn_Za8VKk-cwomzmbEsNywPt5A"
}
HTTP status code 404
Body
Type: application/json
Example:
{
"message": "Member with such an email could not be found"
}
/auth/signin
Authenticate member by email & password. Returns the access token to login (impersonate) as a member. Please note the base URL for this endpoint is https://app.officernd.com/api/{version} instead of https://app.officernd.com/api/{version}/organizations/{orgSlug}.
Body
Type: application/json
Example:
{
"username": "miron@officernd.com",
"password": "pa$$word"
}
HTTP status code 200
Body
Type: application/json
Example:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjU4YTMwYmU5NGVhNjhlZTZjMzk0ZGUzMSIsImlhdCI6MTQ4NzA4MTE4NCwiZXhwIjoxNTE4NjE3MTg0fQ.6wjmGAEqojB7Uc473SNCIFIxQgJv_GusgK2w64oDW-I"
}
HTTP status code 404
Body
Type: application/json
Example:
{
"message": "Unauthorized"
}
Signup
Creates a new user credentials. Those credentials can be used later to login into OfficeRnD members portal to be exchanged for a bearer token and make requests as the user in hand.
/signup
Body
Type: application/json
Example:
{
"email": "user-email@mail.com",
"password": "user-password",
"name": "Full User Name",
"company": "Company name"
}
HTTP status code 200
Body
Type: application/json
Example:
{
"email": "user-email@mail.com",
"name": "Full User Name",
"company": "Company name"
}
Checkout
Checkout and checkout summary endpoints allow you to create a new order or get the preview of an order. Depending on the organization settings and the current member status they may create an invoice, charge the member and send an invoice automatically.
Here is the list of all properties the checkout request object has:
Checkout Request
Property | Type | Required | Description |
---|---|---|---|
plans | PlanRequest | false | A list of plan request definitions to be added to the order. |
fees | FeeRequest | false | A list of fee request definitions to be added to the order. |
discountCodes | [string] | false | 0 or 1 discount codes to be applied. |
Plan Request
Property | Type | Required | Description |
---|---|---|---|
plan | id | true | The ID of the membership plan to be ordered. |
startDate | date | true | The start date of the ordered membership. |
endDate | date | false | The end date of the ordered membership. |
Fee Request
Property | Type | Required | Description |
---|---|---|---|
plan | id | true | The ID of the membership plan to be ordered. |
date | date | true | The date the one-off is ordered for. |
/checkout
Body
Type: application/json
Example:
{
"plans": [{
"plan": "5aa14137890301e40c912ce5",
"addOns": [],
"startDate": "2018-04-20T00:00:00.000Z"
}],
"fees": [],
"discountCodes": [ "10percent" ]
}
HTTP status code 200
/checkout/summary
Get a checkout summary.
Body
Type: application/json
Example:
{
"plans": [{
"plan": "5aa14137890301e40c912ce5",
"addOns": [],
"startDate": "2018-04-20T00:00:00.000Z"
}],
"fees": [],
"discountCodes": [ "10percent" ]
}
HTTP status code 200
Body
Type: application/json
Example:
{
"stats": {
"taxType": "included",
"invoiceLines": [
{
"description": "Hotdesk plan, £250.00, Apr 20, 2018 - May 18, 2018 (prorated 29 days)",
"discount": 10,
"account": "5aa138f97adac0140057ad0d",
"vatPercent": 0,
"taxRate": "5aa138f97adac0140057ad13",
"plan": "5aa14137890301e40c912ce5",
"membership": "5addc9b4548cb55832f8ef34",
"quantity": 0.9534,
"unitPrice": 250,
"price": 238.35,
"discountAmount": 23.84,
"baseUnitPrice": 250,
"vatAmount": 0,
"subtotal": 214.52,
"baseTotal": 214.52,
"total": 214.52
}
],
"discount": 23.84,
"baseTotal": 214.52,
"subtotal": 214.52,
"vatAmounts": [],
"total": 214.52,
"payableAmount": 214.52
},
"billingFlow": {
"filter": {
"status": "active"
},
"options": {
"invoiceImmediatelly": false,
"requireCreditCard": false,
"chargeImmediatelly": false,
"sendInvoice": false
}
}
}
Subscribe
Create a new member and/or company in the organization in hand and link it to the current user.
/subscribe
Body
Type: application/json
Example:
{
"name": "John Doe",
"team": "OfficeRnD",
"phone": "+441234567",
"office": "5a0ef3c67f0c3716001c74e7",
"properties": {},
"billing": {},
"address": {}
}
User
All resources under /user
are meant for members only and not for admins. They should be called with a token issued with member credentials using the auth/signin endpoint.
/user/visits
Visit objects allow you to describe particular visits of a visitor. They are charachterized by a start and end date and hold a reference to the visitor.
Visits can be created, queried, updated and deleted provided that they are owned by the member currently logged in.
Here is the list of all properties the visit object has:
Visit
Property | Type | Required | Description |
---|---|---|---|
start | date | true | The start time of the visit |
end | date | true | The end time of the visit |
visitor | Visitor | true | Description of the visitor |
Retrieve all visit objects for the currently logged member
Query Parameters
- start.$gt: (date)
All items starting after the pointed date
- start.$lt: (date)
All items starting before the pointed date
- end.$gt: (date)
All items ending after the pointed date
- end.$lt: (date)
All items ending before the pointed date
- $populate: (string)
Comma-separated list of all properties to be populated. Only id references can be populated. Refer to Populating queries section for more information.
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id" : "5ce67f5a872ac4000fc734f1",
"team" : "5ce4fbc925e9e8000e6222b7",
"member" : "5ce4fc6825e9e8000e6223c9",
"start" : "2019-05-05T10:00:00.000Z",
"end" : "2019-05-05T12:00:00.000Z",
"visitor" : "5ce4fc6825e803200e62233",
"createdBy" : "5cab0cb00b15ae000f2d1c54",
"createdAt" : "2019-05-23T11:09:14.633Z
}]
Add multiple visit objects.
Body
Type: application/json
Example:
[{
"start" : "2019-05-05T10:00:00.000Z",
"end" : "2019-05-05T12:00:00.000Z",
"visitor" : "5ce4fc6825e803200e62233"
}]
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id" : "5ce67f5a872ac4000fc734f1",
"team" : "5ce4fbc925e9e8000e6222b7",
"member" : "5ce4fc6825e9e8000e6223c9",
"start" : "2019-05-05T10:00:00.000Z",
"end" : "2019-05-05T12:00:00.000Z",
"visitor" : "5ce4fc6825e803200e62233",
"createdBy" : "5cab0cb00b15ae000f2d1c54",
"createdAt" : "2019-05-23T11:09:14.633Z
}]
/user/visits/{visitId}
Retrieve an individual visit object.
Get the visitor object with id = {visitId}
URI Parameters
- visitId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
{
"_id" : "5ce67f5a872ac4000fc734f1",
"team" : "5ce4fbc925e9e8000e6222b7",
"member" : "5ce4fc6825e9e8000e6223c9",
"start" : "2019-05-05T10:00:00.000Z",
"end" : "2019-05-05T12:00:00.000Z",
"visitor" : "5ce4fc6825e803200e62233",
"createdBy" : "5cab0cb00b15ae000f2d1c54",
"createdAt" : "2019-05-23T11:09:14.633Z
}
HTTP status code 404
Body
Type: application/json
Example:
{
"message": "Visit not found"
}
Update a visit object with id = {visitId}
URI Parameters
- visitId: required (string)
Body
Type: application/json
Example:
{
"start": "2019-05-05T10:30:00.000Z",
}
HTTP status code 200
Body
Type: application/json
Example:
{
"_id" : "5ce67f5a872ac4000fc734f1",
"team" : "5ce4fbc925e9e8000e6222b7",
"member" : "5ce4fc6825e9e8000e6223c9",
"start" : "2019-05-05T10:30:00.000Z",
"end" : "2019-05-05T12:00:00.000Z",
"visitor" : "5ce4fc6825e803200e62233",
"createdBy" : "5cab0cb00b15ae000f2d1c54",
"createdAt" : "2019-05-23T11:09:14.633Z
}
HTTP status code 404
Body
Type: application/json
Example:
{
"message": "Visitor not found"
}
Remove the visit object with id = {visitId}
URI Parameters
- visitId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
{
"_id" : "5ce67f5a872ac4000fc734f1",
"team" : "5ce4fbc925e9e8000e6222b7",
"member" : "5ce4fc6825e9e8000e6223c9",
"start" : "2019-05-05T10:30:00.000Z",
"end" : "2019-05-05T12:00:00.000Z",
"visitor" : "5ce4fc6825e803200e62233",
"createdBy" : "5cab0cb00b15ae000f2d1c54",
"createdAt" : "2019-05-23T11:09:14.633Z
}
HTTP status code 404
Body
Type: application/json
Example:
{
"message": "Visit not found"
}
/user/visitors
Visitor objects allow you to associate visitors to members part of your space and track their visits.
Visitors can be created, queried, updated and deleted provided that they are owned by the member currently logged in.
Here is the list of all properties the visitor object has:
Visitor
Property | Type | Required | Description |
---|---|---|---|
name | string | true | The full name of the visitor |
string | true | The email address of the visitor | |
description | string | false | Description of the visitor |
phone | string | false | The phone number of the visitor |
Retrieve all visitor objects for the currently logged member
Query Parameters
- phone: (string)
The phone number to filter by
- name: (string)
The exact name of the item you are looking for.
- office: (string)
Filter by office id.
- $populate: (string)
Comma-separated list of all properties to be populated. Only id references can be populated. Refer to Populating queries section for more information.
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id" : "5ce67f5a872ac4000fc734f1",
"team" : "5ce4fbc925e9e8000e6222b7",
"member" : "5ce4fc6825e9e8000e6223c9",
"name" : "Joe Doe",
"email" : "joe@doe.com",
"phone" : "00123456789",
"description" : "One-time visit for a booking",
"createdBy" : "5cab0cb00b15ae000f2d1c54",
"createdAt" : "2019-05-23T11:09:14.633Z
}]
Add multiple visitor objects.
Body
Type: application/json
Example:
[{
"name" : "Joe Doe",
"email" : "joe@doe.com",
"description": "Joe visiting for the Sales discussions.",
"phone": "00123456789"
}]
HTTP status code 200
Body
Type: application/json
Example:
[{
"_id" : "5ce67f5a872ac4000fc734f1",
"team" :"5ce4fbc925e9e8000e6222b7",
"member" :"5ce4fc6825e9e8000e6223c9",
"name" : "Joe Doe",
"email" : "joe@doe.com",
"description": "Joe visiting for the Sales discussions.",
"phone": "00123456789"
"createdBy" : "5cab0cb00b15ae000f2d1c54",
"createdAt" : "2019-05-23T11:09:14.633Z"
}]
/user/visitors/{visitorId}
Retrieve an individual visitor object.
Get the visitor object with id = {visitorId}
URI Parameters
- visitorId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
{
"_id" : "5ce67f5a872ac4000fc734f1",
"team" :"5ce4fbc925e9e8000e6222b7",
"member" :"5ce4fc6825e9e8000e6223c9",
"name" : "Joe Doe",
"email" : "joe@doe.com",
"description": "Joe visiting for the Sales discussions.",
"phone": "00123456789"
"createdBy" : "5cab0cb00b15ae000f2d1c54",
"createdAt" : "2019-05-23T11:09:14.633Z"
}
HTTP status code 404
Body
Type: application/json
Example:
{
"message": "Visitor not found"
}
Update the visitor object with id = {visitorId}
URI Parameters
- visitorId: required (string)
Body
Type: application/json
Example:
{
"phone": "00456789",
}
HTTP status code 200
Body
Type: application/json
Example:
{
"_id" : "5ce67f5a872ac4000fc734f1",
"team" :"5ce4fbc925e9e8000e6222b7",
"member" :"5ce4fc6825e9e8000e6223c9",
"name" : "Joe Doe",
"email" : "joe@doe.com",
"description": "Joe visiting for the Sales discussions.",
"phone": "00456789"
"createdBy" : "5cab0cb00b15ae000f2d1c54",
"createdAt" : "2019-05-23T11:09:14.633Z"
}
HTTP status code 404
Body
Type: application/json
Example:
{
"message": "Visitor not found"
}
Remove the visitor object with id = {visitorId}
URI Parameters
- visitorId: required (string)
HTTP status code 200
Body
Type: application/json
Example:
{
"_id" : "5ce67f5a872ac4000fc734f1",
"team" :"5ce4fbc925e9e8000e6222b7",
"member" :"5ce4fc6825e9e8000e6223c9",
"name" : "Joe Doe",
"email" : "joe@doe.com",
"description": "Joe visiting for the Sales discussions.",
"phone": "00456789"
"createdBy" : "5cab0cb00b15ae000f2d1c54",
"createdAt" : "2019-05-23T11:09:14.633Z"
}
HTTP status code 404
Body
Type: application/json
Example:
{
"message": "Visitor not found"
}
Signup Flow
The user signup flows goes through 3 steps:
- Signup step - see the /signup endpoint - creates a set of user credentials in OfficeRnD (doesn't require authentication)
- Subscribe step - see the /subscribe endpoint - creates a member & a company inside the correspnding organization and links them to the current user (requires bearer token)
- Checkout step - see the /checkout endpoint - adds memberships and one-off charges to the member account and triggers a purchase flow (that might result in issuing an invoice and charging the card on file)
Hosted Payment Gateway
OfficeRnD Hosted Payment Gateway extensibility allows for Developers to implement payment gateway integration that is not provided out-of-the-box.
Configure your hosted payment gateway
To start developing and testing your hosted payment gateway, enable and configure it inside the OfficeRnD admin console, following these steps:
- Login to the OfficeRnD admin console as an
Owner
and navigate to Settings > Integrations. - Locate the Hosted Payment Gateway under the Payment Gateways section.
- Click the Activate link and then Configure
A dialog is shown that allows you to input and get all necessary information for the integration to work.
- The Name field is used to define the name of the integration. If you input Cool Payments, the customer will see that as Pay with Cool Payments
- The Secret field is read-only. Its value is initially hidden and you can retrieve it by hitting the "eye" button next to it. You must use the value of this field to verify requests and sign your responses. This secret should be used only on your servers and should never be shown, served or shared with the end user.
- The Gateway URL should be a fully formed url i.e. -
https://your.integration.url/path
. You can put parameter placeholders that will be populated when OfficeRnD initiates the customer redirect request. The placeholders that you can use are as follows:{{ amount }}
,{{ reference }}
,{{ transactionId }}
,{{ signature }}
,{{ redirectUrl }}
.- Example:
https://pay.cool-payments.com/hosted?amount={{ amount }}&ref={{ reference }}&tId={{ transactionId }}&signature={{ signature }}&redirectUrl={{ redirectUrl }}
- Example:
How it works
- The customer chooses to pay with the hosted payment gateway, configured in OfficeRnD.
- OfficeRnD redirects the customer to your gateway's URL, submitting the correct Request parameters. Your gateway must verify the signature parameter value and present a hosted payments page to the customer.
- The customer payment is processed on the hosted payment page that you provide.
- Upon completing the payment, customers should be redirected back to the redirectUrl with all required Response parameters in the query string. This includes the signature. If the payment was successful, the status parameter should have a
success
value. Otherwise the status should be -fail
. - OfficeRnD accounts for the payment to the respective invoice and shows a success or failure message the customer.
Developing your hosted payment gateway integration
OfficeRnD enables you to extend the system so that you can process payments using a custom-developed payment page. OfficeRnD checkout will redirect the customer to the configured payment gateway page. The page should be able to handle predefined request parameter values. After the customers complete the payment, they will be redirected back to OfficeRnD, with predefined response parameter values.
It is the responsibility of the payment gateway to build the page that handles the payment requests.
Request and Response signing mechanism
All requests and responses must be signed and verified using HMAC-SHA256. The signing should use the following rules:
For
key
you must use the value if the Secret field defined in your integration configuration page.The value of
message
must be a string comprised of the values of all key parameters concatenated by the pipe symbol ('|') as a separator.The key parameters for the request are
amount
,reference
andtransactionId
.Example: If the request amount is 12.34, it has a reference INV-001 and a transactionId - 6f47c19b-eb9d-4ee5-a144-19f7b9263183, then the message should be the following string:
12.34|INV-001|6f47c19b-eb9d-4ee5-a144-19f7b9263183
The key parameters for the response are
status
andtransactionId
.Example: If the response status is success and its transactionId is 6f47c19b-eb9d-4ee5-a144-19f7b9263183, then the message should be the following string:
`success|6f47c19b-eb9d-4ee5-a144-19f7b9263183`
- Resulting codes must be hex-encoded and passed as value of signature parameter. Make sure to use case-insensitive comparison when verifying the provided signature values.
Assuming your HMAC key is "11111111-1111-1111-1111-111111111111", the signatures of the example messages should be:
12.34|INV-001|6f47c19b-eb9d-4ee5-a144-19f7b9263183
-617b0b7d29e57db460208904f0e64c11092872d86faf581598c3ba9b8b0e00ae
success|6f47c19b-eb9d-4ee5-a144-19f7b9263183
-2c6d0e09f76011cebf9073a4fa08d752e5c5c440e750c6ee1f8ad5d5cce70bad
Request parameters
Name | Type | Required | Description |
---|---|---|---|
amount | number | true | The amount that needs to be charged. |
reference | string | true | A reference string that clarifies what the charge is for (i.e. INV-001). |
transactionId | string | true | A unique identifier that references the transaction. |
signature | string | true | A signature generated from the amount, reference and transactionId. |
redirectUrl | string | true | A url to redirect back when charge is processed or canceled. |
Response parameters
Name | Type | Required | Description |
---|---|---|---|
status | string | true | The status of the transaction - values should be either success or fail . |
transactionId | string | true | The unique identifier that was passed in the initial request. |
signature | string | true | A signature generated from the status and transactionId. |
error | string | false | An error message with more details about the reason of the failure. |
Webhooks
Learn how to create webhooks to be notified automatically when certain events in the OfficeRnD account occur, and set up integrations.
Overview
Webhooks allow developers to build integrations, which are notified when a certain event happens in OfficeRnD. When such event is triggered, the webhook's configured URL will receive a HTTP POST request. Webhooks can be used to update a dataset in a local service deployment; to notify the change of a linked entity in another 3rd party service; or to send messages to a set of email addresses; and more.
What are webhooks
The webhooks are a communication mechanism which delivers real-time notifications whenever a given event happens. The webhooks automatically 'push' a notification immediately when an event in the system occurs. In comparison, integration implementations can also poll (a 'pull' communication method) - periodically check for data additions or changes.
Important: We do not recommend polling as a viable approach to periodically pull large datasets. Normally changes in the data occur to a handful of entries and not to all. With that in mind, it is suboptimal to pull an entire dataset instead of iteratively applying the changes as they happen in OfficeRnD. Additionally polling requests can hit the rate limit and be cut off.
The webhook has an associated URL (i.e., https://your.integration.url/webhooks). The notification payloads are delivered to the URL using HTTP POST requests. The webhook notification payload contains all relevant information about what happened in OfficeRnD, including the type of event and the data changes associated with that event. The webhook receiver uses the event details and decides what to do with it - such as to deactivate the associated Salesforce entity.
Register a webhook
Developers can register webhooks which deliver notifications for one, a few, or for all events in their OfficeRnD account.
As a prerequisite to registering a webhook in OfficeRnD, you need to build your own custom endpoint on your server. Additionally you must ensure the server URL is secure (i.e. it begins with https://
), otherwise you won't be allowed to register the webhook endpoint.
Following is an example code of a simple HTTP Node.js server which listens for POST requests at the /webhooks/callback
endpoint.
const app = require('express')();
const bodyParser = require('body-parser');
app.use(bodyParser.raw({ type: 'application/json' }));
app.post('/webhooks/callback', (req, res) => {
const { body, rawBody, headers } = req;
const { eventType, data } = body;
switch (eventType) {
case 'company.paymentdetails.removed':
// ...
break;
case 'member.paymentdetails.removed':
// ...
break;
case 'member.removed':
case 'company.removed':
// ...
break;
default:
// unhandled event
console.log(`Event ${eventType} was not handled.`);
}
}
app.listen(3000, () => console.log('Listening on 3000'));
You can find the complete node.js example in the webhooks-demo GitHub repo
Note: The example above is purely for illustrative purposes. The technology of choice and server implementation details may differ greatly.
Register the webhook in Settings > Developer Tools > Webhooks. You'll be asked for the following
Endpoint URL
- the endpoint of your server where HTTP POST requests will be madeDescription
- an optional description allowing you to describe the purpose of the webhookEvents to send
- the list of events for which requests will be made to the endpoint
Receiving webhook notifications
For each event occurrence, the webhook is registered for, the webhook data is POSTed to your endpoint in JSON format.
The webhook notification request payload will be a JSON comprising the following fields:
Property | Type | Description |
---|---|---|
event | string | The unique identifier of the event instance |
eventType | string (system event type) | The event type that occurred in OfficeRnD |
data | object | The event payload describing the event |
data.object | object | The resource that has been added/changed/removed |
data.previousAttributes | object | The field is only present for updated events. The object describing all changed attributes and their previous values |
data.\<parent-resource-name> | string | The field is only present for subresource events to identify the parent resource - e.g. data.invoice , data.member , etc. |
createdAt | string (ISO Date) | The time of the event occurrence. |
Below is an example of a webhook notification delivered when the payment card details of a company are deleted:
{
"event": "5f8d6c9747120500116899e4",
"eventType": "company.paymentdetails.removed",
"data": {
"object": {
"_id": "5f86de9c1e74e400d416467c",
"card": {
"id": "card_<redacted>",
"name": "E Corp.",
"brand": "Visa",
"funding": "credit",
"exp_month": 11,
"exp_year": 2022,
"country": "US",
"cvc_check": "pass",
"last4": "1111"
},
"provider": "stripe",
"providerId": "<redacted>",
"sourceId": "card_<redacted>",
"authorization": {
"authProviderId": "seti_<redacted>",
"status": "not_required"
},
"createdAt": "2020-10-14T11:18:52.130Z",
"createdBy": "5e131db18edb280010c8d4ef"
},
"company": "5e1d8bd37f8759001051b0e8"
},
"createdAt": "2020-10-19T10:38:15.127Z"
}
Similarly, when a member is removed, a webhook notification for the member.removed
event is delivered:
{
"event": "5f8715ca1e74e400d4164ea6",
"eventType": "member.removed",
"data": {
"object": {
"_id": "5e8c9df14d46c7001064c3ef",
"status": "active",
"calculatedStatus": "drop-in",
"team": "5e8c9eb94d46c7001064c427",
"office": "5e1d8bd37f8759001051b0e2",
"name": "Jonathan Brown",
"description": "",
"image": null,
"organization": "5e1d8bd37f8759001051b0ca",
"email": "ingadget@ecorp.com",
}
},
"createdAt": "2020-10-14T15:14:18.042Z"
}
Responding to webhook notifications
To acknowledge that a notification has been received, your endpoint must respond with a 2xx
HTTP status code to OfficeRnD. Sending a response body is not required.
Additionally to the response status code, your endpoint must also respond quickly - prior to running any complex logic upon receipt of a notification, ensure you've returned a 2xx
HTTP status code.
If your endpoint doesn't respond accordingly with the correct status code or in a timely manner, the webhook notification attempt will be treated as a failure.
Note: Currently failed webhook notification attempts will not be automatically retried
Test your webhooks
After configuring your server to be able to receive POST requests, and registering your webhook in OfficeRnD, it's time to test it. To that end, OfficeRnD's webhook details view provides you with a list of all webhook notification delivery attempts.
As your endpoint receives notification payloads asynchronously, its failures may not be immediately obvious. We generally recommend verifying that the webhook notification attempts are successful after making any changes to the server.
Secure your webhooks
Once your server is configured to receive notification payloads, it'll be open for any requests sent to it. For security reasons, you should verify all incoming requests and allow only those coming from OfficeRnD. All webhook notification payloads sent by OfficeRnD are signed and the signature is included in the officernd-signature
header of the request. Using the webhook's secret
and the payload body you should verify the computed signature matches that in the header.
Webhook secrets
Before you can verify signatures, you need to retrieve your endpoint’s secret from your Webhook's details view. Select a webhook that you want to obtain the secret for, then click the Click to reveal
button.
Webhook secrets are unique for each registered webhook endpoint. If you register several webhooks, you will need to verify the signatures for each with their respective webhook's secret.
Verifying payloads from OfficeRnD
OfficeRnD creates a hash signature with each payload. The signature is included in the headers of each webhook notification request as officernd-signature
. For extra security, in addition to the signature, each header contains a timestamp (prefixed by t=
) which is used in the hash computation.
The officernd-signature
header has two fields, separated with ,
: t
and signature
An officernd-signature
header will look similar to this:
officernd-signature: t=1602237976,signature=099618786b221c0bd88346a71e01c8deec60007c2db2ead5975284d4e957b8f5
Signatures are computed using an HMAC hex digest with SHA-256. The raw request body and timestamp are joined by a dot .
and HMAC hashed. In order to verify the signature, you will have to repeat the process with the incoming data and compare your result to the signature in the header.
Extract the timestamp and signatures
- Split the header using the
,
as the separator. - Then split the resulting elements (
t=....
andsignature=...
) using the=
character as the separator. This will give you two pairs to work with - the timestamp and its value, and the signature and its hash. - Verify the payload timestamp and the current timestamp difference is within your tolerance
- Split the header using the
Prepare the payload to sign. Concatenate the pieces in the following order:
- The raw body payload (as a string)
- The dot character
.
- The timestamp value
Compute the expected signature
- Compute an HMAC with the SHA-256 hash function
- Use the webhook secret as the key
- Use the concatenated string from step 2 as the message
Compare the signatures
- Compare the header signature from step 1 to the expected signature from step 3
You can find a complete example of the signature verification flow in the webhooks-demo GitHub repo
System event types
Below is a complete list of all types of events OfficeRnD can currently send webhook notifications for.
All event names follow a common pattern resource.(optional)subresource.operation
such as invoice.created
or invoice.charge.created
. The supported operations are CUD - created
, updated
, deleted
. This way it should be easy to understand what an event name corresponds to within OfficeRnD.
resource | operations | events | description |
---|---|---|---|
company | created updated removed | company.created company.updated company.removed | |
company.paymentdetails | created removed | company.paymentdetails.created company.paymentdetails.removed | Occurs when payment details (i.e. card details, bank details, etc.) are added or removed for a company. |
member | created updated removed | member.created member.updated member.removed | |
member.paymentdetails | created removed | Occurs when payment details (i.e. card details, bank details, etc.) are added or removed for a member. | |
invoice | created updated removed | invoice.created invoice.updated invoice.removed | Occurs when an Invoice, Credit Note or Overpayment is created, updated, or removed. Property data.object.documentType contains information about the type of document. |
invoice.charge | created updated removed | invoice.charge.created invoice.charge.updated invoice.charge.removed | Occurs when an Invoice receives a payment, when the payment status is updated, or when the payment is manually removed. |
invoice.allocation | created removed | invoice.allocation.created invoice.allocation.removed | Occurs when an Invoice is allocated a credit from a credit note, or when an allocation is manually removed. |
fee | created updated removed | fee.created fee.updated fee.removed | |
membership | created updated removed | membership.created membership.updated membership.removed | |
booking | created updated removed | booking.created booking.updated booking.removed | |
contract | created updated removed | contract.created contract.updated contract.removed | |
ticket | created updated removed | ticket.created ticket.updated ticket.removed |