Genie Partner Integration

Table of Contents

  1. Setup
  2. Branding
  3. Authentication
  4. API
  5. Webhook
  6. Usage Limits

Setup

Before starting your integration, you need to make sure you have the following items:

  1. A client ID (provided by Genie)
  2. A client Secret (provided by Genie)
  3. A base API URL to make API calls (provided by Genie)
  4. An API Key

The base API URL will be of the form yourcompanyname.smartgarage.systems. Note that all three of these values are for development purposes only. Once you have integrated your system, you can reach out to us to begin migration into the production system. At that point, the above items will be regenerated for you and sent via a secure transfer mechanism.

If you do not have any of these items, please reach out to us so that we can set this up for you.

Branding

Our system supports two brands, Overhead Door Company (OHD) and Genie Company. The two apps are OHDAnywhere and Aladdin Connect, respectively. In order for us to show the correct branding to the users, during the linking phase we will ask you to direct them to the correct login page based on a path parameter of either 'ohd' or 'aladdin' (described in detail below). After the initial linking phase (OAuth2.0 flow), the brand will no longer be necessary to track as all requests and callbacks will be done with the same system.

OAuth 2.0 Flow

In order to gain access to a user's devices, your application will need to use the OAuth 2.0 protocol. First, make sure you have everything described in the Setup section above.

The OAuth 2.0 flow is as follows:

User Login

When a user requests to add their Genie device to your service, they should be forwarded to the following login URLs depending on brand:

Using the following parameters:

NOTE: response_type parameter should be set to the literal value code exactly as above.

Token Exchange

After the user logs in to their Genie account on that page, our server will call the redirect_uri you supplied above with the following format:

<redirect-uri>?code=<authorization-code>&state=<state>

The <authorization-code> parameter will be a one time use code that you can use to get an access token. The <state> parameter will be whatever you supplied in the <state> parameter above.

Once you have the authorization code supplied in the above callback, you can use it to make the following request to our server:

POST https://<base-url>/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
client_id=<client-id>
client_secret=<client-secret>
code=<authorization-code>
redirect_uri=<redirect-uri>

Using the following parameters:

You will receive a 200 status JSON response like the following:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTc2MjM5MDIyfQ.ZgYY-8GOvkCjnSadE0kXx_Ddu7h5bNMamsiud8y9ne4",
  "token_type": "Bearer",
  "expires_in": 3600
}

The access_token is the token that you will use to access our endpoints (described in the API section below). This is a short-lived token (1 hour), so you will also need to store the refresh_token so that you can get new tokens when needed (described below).

Refreshing Tokens

When a token has expired, you will need to get a new token using the stored refresh_token provided in the initial flow above. To do this, you can make the following call:

POST https://<base-url>/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
client_id=<client-id>
refresh_token=<refresh-token>

Using the following parameters:

You will receive a 200 status JSON response like the following:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
  "token_type": "Bearer",
  "expires_in": 3600
}

NOTE: We will rotate refresh tokens once a year. In this case, you will get a new refresh token (example response below). You should store this new refresh token and discard the old one.

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTc2MjM5MDIyfQ.ZgYY-8GOvkCjnSadE0kXx_Ddu7h5bNMamsiud8y9ne4",
  "token_type": "Bearer",
  "expires_in": 3600
}

API

The next section gives an overview of the API for interacting with Genie devices. This assumes you have implemented the above described OAuth 2.0 flow and are able to get access tokens.

Authorization

Every endpoint described in this section requires an authorization header like:

Authorization: Bearer <access-token>

Where the <access-token> is the token you received in the above described OAuth 2.0 flow. You should have an access token for each user that has integrated with Genie, as this token will be used to identify which Genie user you are making actions on behalf of.

You will also need to provide the API KEY provided in the setup like:

X-API-KEY: <api-key>

Device Discovery

Device discovery can be used to get a list of all the Genie devices in the user's account. This is typically used when the user initially signs into their Genie account. You can also poll this every 12-24 hours to make sure you have all the devices in their account. However, this is not meant to be used to synchronize the state of the devices, so there should not be any need to call this more frequently than every 12-24 hours. There are better methods of device synchronization described later in this section.

You can access this resource by making a GET call to the /devices endpoint. The response will look like:

[
  {
    "ownership": "owner",
    "name": "Genie Device 1",
    "id": "A1B2C3D4E5F6",
    "status": "online",
    "productFamily": "idcm",
    "doors": [
      {
        "name": "Genie Door",
        "index": 1,
        "link_status": "online",
        "battery": 100,
        "status": "closed"
      }
    ]
  },
  {
    "ownership": "owner",
    "name": "Genie Device 2",
    "id": "F6E5D4C3B2A1",
    "status": "online",
    "productFamily": "edcm",
    "doors": [
      {
        "name": "Door 1",
        "index": 1,
        "link_status": "online",
        "battery": 100,
        "status": "closed"
      },
      {
        "name": "Door 2",
        "index": 2,
        "link_status": "online",
        "battery": 100,
        "status": "open"
      }
    ]
  }
]

As mentioned above, this is only meant as part of a "discovery" phase. Further synchronization can be done using other methods described later in this section.

Door State

After doing the initial discovery described above, you can request the state of a specific door by making a GET call to the following endpoint:

/devices/{deviceId}/doors/{doorIndex}

Where {deviceId} is the id field from the above discovery response, and the {doorIndex} is the index field from the specific door you would like to receive the state for. This will give a response like:

{
  "linkStatus": "connected",
  "device_status": "online",
  "fault": "none",
  "id": "A1B2C3D4E5F6",
  "door_index": 1,
  "status": "closed"
}

Door State Values

The following values are possible for the device_status:

The following values are possible for the fault:

The following values are possible for the status:

The following values are possible for the linkStatus:

Command

In order to operate a door, you must send a "door command" using a POST call to the following endpoint:

/devices/{deviceId}/doors/{doorIndex}/command

Where {deviceId} is the id field from the above discovery response, and the {doorIndex} is the index field from the specific door you would like to operate. You must also send a JSON body like the following:

{
  "command": "<door-command>"
}

where <door-command> is either open or close. If the command is received, you will receive a 200 status response. This 200 status only means we received the command. The actual opening or closing of the door will be sent later using the webhook endpoint described below.

User

If you need to get info about the Genie user account, you can send a GET request to the endpoint /user. Note that no ID is needed since the access token in the authorization header will be used to identify the user. The JSON response will look like the following:

{
  "name": "Genie User",
  "id": "643261cf-7252-459b-9ed6-61ab76181c0d",
  "email": "example@genie.com"
}

Webhook

The above section describes how you can use our API to issue commands and synchronize state, but what about operations that happen outside of your application? We provide a way for you to listen to those by allowing you to link a "webhook" URL, which is a callback URL that we will send requests to whenever a user adds a device, removes a device, or a device's state changes. In this way, you can get real time updates to a user's devices without having to poll the above API. Because of this, we recommend you do not do any polling of the above API more frequently than once or twice a day. Ideally, after the initial discovery phase, you should be able to keep all device state in sync by setting up this webhook.

The URL you provide to us for this webhook will be called with a POST request every time a device is added or removed, or whenever linked device's state changes. For add and update events, there are enumerated values sent in the event body. They are defined at the bottom of this section. Next we will give examples of the three event types this webhook will be called with (these payloads will be the body of the request).

Insert

This event will be sent any time a new device is added to a user's account.

{
  "eventType": "Insert",
  "inserted": [
    {
      "device_id": "FC45C315EE248",
      "email": "genie@test.com",
      "door_index": 1,
      "door_status": 4,
      "link_status": 3,
      "device_status": 1,
      "timestamp": "2023-05-24T14:58:38.188Z"
    }
  ],
  "updated": [],
  "removed": []
}

Note that the inserted field is an array, as there may be multiple insert events sent in a single request. These events contain enumerated values for the door status, link status and device status fields. Those enumerations are defined later in this section.

Update

This event will be sent when any of the fields are changed. Note that a single event can represent more than one change.

{
  "eventType": "Update",
  "inserted": [],
  "updated": [
    {
      "device_id": "FC45C315EE248",
      "email": "genie@test.com",
      "door_index": 1,
      "door_status": 4,
      "link_status": 3,
      "device_status": 1,
      "fault": 0,
      "timestamp": "2023-05-24T14:58:38.188Z",
      "last_command_id": 1725036098,
      "last_command_outcome": "SUCCESS" // OPTIONAL
    }
  ],
  "removed": []
}

Optional Command Outcome Field

If (and only if) there is an ongoing command, the last_command_outcome field will be one of the following values:

SUCCESS: The command was successful IN_PROGRESS: The command is still in progress TIMEOUT: The command timed out DUPLICATE: The door was already in the desired state NOT_NEEDED: The door was already in the desired state BUSY: The door was already in motion OBSTRUCTION: There was something in the way of the door sensors UNKNOWN: The command failed for an unknown reason

If there is not an ongoing command, the last_command_outcome field will not be present.

Note that the updated field is an array, as there may be multiple update events sent in a single request. These events contain enumerated values for the door status, link status and device status fields. Those enumerations are defined later in this section.

Remove

This event will be sent any time a device is removed from a user's account.

{
  "eventType": "Remove",
  "inserted": [],
  "updated": [],
  "removed": [
    {
      "device_id": "FC45C315EE248",
      "door_index": 1,
      "email": "genie@test.com"
    }
  ]
}

Note that the removed field is an array, as there may be multiple remove events sent in a single request.

Triggering of Events

Insert Event

You can trigger this event by adding a new device to your account using our mobile app.

Update Event

You can trigger this event by the following methods:

  1. Opening or closing a door using the wall console button or an RF remote. Note that this will not include a last_command_outcome field.
  2. Opening or closing a door using the mobile app. This will include a last_command_outcome field.
  3. Opening or closing a door using the API described above. This will include a last_command_outcome field.
  4. Unplugging a device or otherwise removing it from network connectivity. This can take up to 3 minutes for our system to detect, after which you will receive an update event with device status as offline (0).
  5. If you have a door with STB sensors (laser sensors), operate the door using the mobile app (or the API) with the sensors blocked twice in a row. This should result in consecutive failed remote commands. After the second failure, the device will go into "UL Lockout" mode, which will be sent in an update event with fault code 1. To clear the fault, you must operate the door using the wall console button or an RF remote successfully twice in a row.
  6. If you have a retrofit device with a door sensor, operate the door using the mobile app (or the API) but do not move the sensor. After approximately 120 seconds, you should see an OPEN_TIMEOUT or CLOSE_TIMEOUT state.

For other fault states, you can set up a meeting with us where we can manually put the device into other fault states and trigger the event for you to test.

Remove Event

You can trigger this event by deleting a device from your account using our mobile app.

Enumerations

Door State

enum {
  UNKNOWN = 0,
  OPEN = 1,
  OPENING = 2,
  OPEN_TIMEOUT = 3,
  CLOSED = 4,
  CLOSING = 5,
  CLOSE_TIMEOUT = 6
}
enum {
  UNKNOWN = 0,
  NOT_CONFIGURED = 1,
  PAIRED = 2,
  CONNECTED = 3,
  SYNC_LOST = 4
}

Device Status

enum {
  ONLINE = 1,
  OFFLINE = 0
}

Device Fault

enum {
  NONE = 0,
  UL_LOCKOUT = 1,
  INTERLOCK = 2,
  NOT_SAFE = 3,
  WILL_NOT_MOVE = 4
}

Usage Limits

In order to ensure our API remains performant and available for all users, we have implemented usage limits on our API. These limits are also designed to limit our financial exposure to abuse of the API. There are two types of limits:

  1. Rate Limits: These limits are applied to the number of requests you can make to our API in a given time period. The default rate limit is 100 requests per second.
  2. Daily Quota: This is the total number of requests you can make to our API in a 24-hour period. The default daily quota is 10,000 requests.

The default limits of 100 RPS and 10,000 RPD should be sufficient for most use cases. By integrating the webhook described above and not polling for state updates, you can typically avoid hitting these limits.

If you find that you need higher limits, please reach out to us to discuss your use case and we will see if we can accommodate your needs.