Akiles API Reference
Akiles API
API Endpoint
https://api.akiles.app/v2
Contact: support@akiles.app
Version: 2.0
Changelog
October 2024
- Expanded documentation of endpoint
POST /members/:id/pins
.
September 2024
Added device
to the API. One highlight is this now allows obtaining the online/offline status and battery level of devices via the API.
- Added endpoints
/devices
- Added endpoints
/products
- Added
device_id
to/gadgets
. - Added webhook signatures.
- Expanded documentation for events.
- Expanded documentation for webhook filters.
June 2024
- Added
/members/:id/cards
endpoints.
April 2023
- Added support for creating, editing, deleting
member_group
s.
October 2021
- Added
/members/:id/pins
endpoints.
July 2020: Version 2.0
- Members can now have multiple email addresses.
- Members can now have multiple magic links.
- A member can have email addresses and magic links at the same time.
- A member can now be associated to multiple groups, with each association having its own start and end date/times.
Having multiple email addresses is useful for hotel room reserves where you have multiple guests in the same room. Now you can create the reserve as a single member that grants access to all guests.
Per-group start/end times is useful for room reservations: the same member can have multiple reservations for different rooms at different times, and he'll only be able to enter each room during the allowed time.
API changes
- Removed
member
fields:type
,email
,member_group_ids
- Removed endpoints
/members/{member_id}/magic_link
- Added endpoints
/members/{member_id}/emails
- Added endpoints
/members/{member_id}/magic_links
- Added endpoints
/members/{member_id}/group_associations
Version 1.0
Initial API release
Versioning
The major version is indicated in the URL. For example https://api.akiles.app/v2
vs https://api.akiles.app/v2
. The minor version is not indicated anywhere, since minor versions only do backwards-compatible changes. A client designed for API version v2.0 should work unmodified on version v2.1, provided it follows the compatibility policy outlined below.
We will release new minor versions with only backwards-compatible changes. We consider the following changes backwards-compatible:
- Adding new endpoints.
- Adding new HTTP methods to existing endpoints.
- Adding new optional fields to request objects.
- Adding new fields to response objects.
- Adding new possible values to
enum
fields. - Relaxing request validation rules. For example, a field couldn't be null before and now it can.
Keep these in mind when developing your integration. For example, when parsing API responses, ignore unknown received fields instead of erroring.
Ocasionally, when backwards-incompatible changes are necessary for the healthy evolution of the platform, we will also release new major API versions. In this case, the previous major version will be supported for at least 6 months after the launch of the new major version.
Integration examples
There are many ways of integrating Akiles into your existing platform or service. There are two main categories:
-Sync integration (non whitelabel): The user end user will login with the Akiles app or website (magic link, without downloading any app). You can still customize and change all the related paramenters such as membership validity, permissions, sites, etc. The integration consists in syncing your existing objects (users, reservations) with Akiles so user creation and management can be fully automatic.
-Whitelabel integration: The end user will be able to access the diferent spaces through a custom experience in your app or website. You can implement any logic that will end up opening a door. This integration has extra cost and is only available with the whitelabel feature. If you plan to publish your integration and make it available to all Akiles customers, we suggest you to use the non whitelabel option.
Sync integration (non whitelabel)
In this kind of integration, the user will use the Akiles app and environment to interact with your spaces. The idea behind this integration is directly creating or editing other Akiles objects from your backend as needed. You will be able to create, edit and delete members or any other object or parameter based on your business logic.
The end user will have to login once the required member is created using any of the valid login methods. You will have to use the already flexible Akiles permission system to implement your logic.
This kind of integration is recommended for Property Management Systems, Booking systems, etc.
Example flow:
- You get a new reservation. Just gather all the relevant details.
- Perform a member create API call. Remember to store the returned member_id in your database
POST https://api.akiles.app/v2/members
Content-Type: application/json;charset=UTF-8
{
"name": "John Doe",
}
- Based on the reservation or booking parameters, associate a group to the recently created member. The group will determine which set of spaces the guest will have access. Do a group association create API request.
POST https://api.akiles.app/v2/members/{member_id}/group_associations
Content-Type: application/json;charset=UTF-8
{
"member_group_id": "mg_3merk33gt1692dk2p2m1",
"starts_at": "2018-03-13T16:56:51.766836837Z",
"ends_at": "2018-03-13T16:56:51.766836837Z",
}
- Choose a login method for the user and send an invite. For temporary applications we recommend using the magic link rather than the email. Do a magic link create API request.
POST https://api.akiles.app/v2/members/{member_id}/magic_links
Content-Type: application/json;charset=UTF-8
{}
- Reveal the link. Warning: Remember that the link is able to grant access to your space. You should protect it and distribute it using a secure channel. In order to reveal the link do a magic link reveal API request.
POST https://api.akiles.app/v2/members/{member_id}/magic_links/{member_magic_link_id}/reveal
Content-Type: application/json;charset=UTF-8
{}
- Send the link to the final user or display it somewhere.
Whitelabel integration
For this kind of integration the user will directly interact with the gadgets, going through a custom experience. You will be responsible for implementing a proper UX/UI and or equivalent business logic.
As an example, the final user will typically login in your platform. You will have to implement all the permissions and conditions before granting the user access to the gadget. Finally, if theese conditions are met, your backend will call the gadget action endpoint in order to perform the action. You will prevously need to call gadget list endpoint in order to get the ids.
Example flow:
- The end user logs in into your app or website
- Your platform checks against your backend or database if the end user meets certain requirements before accessing the action button (have a valid reservation, plan, account, etc...)
- The user clicks the action button. Your backend does the gadget action API call with the gadget id of the gadget related to the space you want the action to happen
POST https://api.akiles.app/v2/gadgets/{gadget_id}
Content-Type: application/json;charset=UTF-8
{
"id": "open",
"name": "Open"
}
- Handle response or error
Authentication types
There are two ways to authenticate to the API: API keys and OAuth.
Use API keys if you're developing integrations that you will only use with your own Akiles organization. This is the easiest and simplest way to get started, and the security benefits of OAuth don't apply to you anyway.
Use OAuth if you want other people to use your integration. The most common use case is if you are developing a SaaS and want your customers to be able to enable your integration with their own Akiles organizations.
Additionally, OAuth is required to get your application/integration listed in the Applications tab in the Akiles admin panel. Listing your application will make it discoverable to all Akiles customers.
API key authentication
API keys are static keys that can be used to make calls to the Akiles API.
To create an API key, login to the Akiles admin panel, and go to API -> API keys and create one. For security, you will only be able to see the full value of the API key once, when creating it.
To use the API key, include an Authorization: Bearer
header with it:
Authorization: Bearer ak_3pbzqfe...
All API keys have full read-write permissions, equivalent to organization administrators. All the actions performed with the API key are attributed to it in the events.
OAuth authentication
OAuth authentication uses the standard RFC-6479 OAuth 2.0 protocol. It allows your app to interactively request access to customers' Akiles organizations and obtain access tokens for them. This makes the experience smoother and more secure, since the customer doesn't have to manually copy and paste a sensitive API key.
This section is not intended to be a full explanation on how OAuth works, just a quick reference to get started. For more info, consult online documentation about OAuth.
Client ID and Client Secret
Your OAuth application needs a Client ID and a Client Secret to operate.
- The Client ID is considered public, it is OK to expose it to frontends (and is in fact necessary to do so).
- The Client Secret must be maintained confidential since it is used to obtain access tokens. Always use it from your server, never send it or expose it to your frontend.
To obtain your application's Client ID and Secret, contact support.
Token types
- Access token: used to make API calls, expires 1 hour after issuance.
- Refresh token: used to obtain new access tokens when they expire.
Authorization flow
- Generate a cryptographically-secure random string
state
and store it somewhere you can access it later (e.g. a cookie) - Start the flow by redirecting the user to the following URL:
https://auth.akiles.app/oauth2/auth?client_id=CLIENT_ID_HERE>&redirect_uri=https://your-app.example.com/redirect&response_type=code&scope=full_read_write offline&state=STATE_HERE
- The Akiles authorization server will prompt the user to choose a user account and organization.
- The Akiles authorization server will ask the user to authorize your requested scopes.
- The user gets redirected to your
redirect_uri
:
https://your-app.example.com/redirect?code=CODE_HERE&scope=full_read_write offline&state=STATE_HERE
- Check the
state
you have received matches the originalstate
you stored. If it doesn't, abort. This is necessary to protect against CSRF / session poisoning attacks. - Exchange the authorization code for the access and refresh tokens.
POST https://auth.akiles.app/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
client_id=CLIENT_ID_HERE
client_secret=CLIENT_SECRET_HERE
code=CODE_HERE
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"access_token": "ACCESS_TOKEN_HERE",
"expires_in": 3599,
"refresh_token": "REFRESH_TOKEN_HERE",
"scope": "full_read_write offline",
"token_type": "bearer"
}
- You're done! Save the access token and refresh token.
You can now use the access token to make API calls on behalf of the user's organization. The access token expires in 1 hour. You can obtain new, fresh access tokens with the refresh token.
Refresh token flow
POST https://auth.akiles.app/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
client_id=CLIENT_ID_HERE
client_secret=CLIENT_SECRET_HERE
refresh_token=REFRESH_TOKEN_HERE
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"access_token": "NEW_ACCESS_TOKEN_HERE",
"expires_in": 3599,
"refresh_token": "NEW_REFRESH_TOKEN_HERE",
"scope": "full_read_write offline",
"token_type": "bearer"
}
Store both the new access and refresh tokens. The old refresh token, once used, is no longer guaranteed to keep working forever.
Scopes
- full_read_write: read-write access to all the objects. Same as a regular API key, or an administrator user.
- full_read_only: read-only access to all the objects.
- offline: allows offline access. You need this to obtain refresh tokens.
Webhooks
Webhook objects are scoped to your OAuth application. That is, webhooks created with API keys or manually from the admin panel won't be visible to you, and webhooks you create won't be visible to API keys or in the admin panel.
All your application's webhooks in an organization will be deleted when your app gets deauthorized.
Even if you have read-only access, you can still create/edit/delete webhooks. This allows you to receive real-time updates in an application that only needs to read data from the Akiles organization.
Pagination
The API uses cursor-based pagination. All list endpoints return data in reverse chronological order, and have the following common structure:
GET parameters
limit
: How many items to fetch, between 1 and 100cursor
: Value ofcursor_next
from a previous request. If not given, it will fetch the most recent items.
Response data
data
: The received items, in a JSON array.has_next
: Whether there are more items to fetch.cursor_next
: Cursor to fetch the next page of items. Only present ifhas_next
is true.
Metadata
Most API objects allow you to attach your own, arbitrary key-value metadata in the metadata
field. You can use it to store references to primary keys of your database, custom states, etc.
Metadata is visible to anyone with admin or API access to your organization. There is a storage limit of 1024 bytes of metadata per object.
metadata
Key-value metadata
Example
{
"key1": "value1",
"key2": "value2"
}
Organizations
Everything in Akiles APIs belongs to an Organization. An organization represents one Akiles customer and can handle multiple sites, gadgets, etc.. API keys are tied to organizations too, and they give access only to the objects belonging to the organization.
organization
id string (organization_id) | Unique identifier. |
name string | Name. |
is_deleted boolean | Indicates if the object has been deleted. |
created_at string (date-time) | Creation time for this object. |
metadata metadata | Key-value metadata |
Example
{
"id": "org_3merk33gt1v9ypgfzrp1",
"name": "SkyCowork",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Get organization
Response Example (200 OK)
{
"id": "org_3merk33gt1v9ypgfzrp1",
"name": "SkyCowork",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Edit organization
metadata metadata | Key-value metadata |
Request Example
{
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Response Example (200 OK)
{
"id": "org_3merk33gt1v9ypgfzrp1",
"name": "SkyCowork",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Sites
A site is a physical place where Akiles gadgets are installed. For example, it can be a building, a floor... Gadgets belong to a particular site.
End users will see one "card" in their app's home screen per site. The site information (email, contact, etc) is displayed in that card.
location
lat number (float) | Latitude in degrees |
lng number (float) | Longitude |
Example
{
"lat": 41.290485,
"lng": 2.1829076
}
site_geo
location location | |
radius number | Radius in meters |
Example
{
"location": {
"lat": 41.290485,
"lng": 2.1829076
},
"radius": 100
}
site_map
location location | |
place_id string | place_id from Google Maps Places API |
address string | Human-readable address |
image_url string | Map image URL, displayed in the site's header in the app. |
Example
{
"location": {
"lat": 41.290485,
"lng": 2.1829076
},
"place_id": "string",
"address": "string",
"image_url": "string"
}
site
id string (site_id) | site ID |
name string | Site name |
organization_id string (organization_id) | organization ID |
geo site_geo | |
map site_map | |
phone string | Phone number. Shown in the site info in the app. |
email string | Email. Shown in the site info in the app. |
info string | Free-form extra information. Shown in the site info in the app. |
timezone string (timezone) | Local time zone of the site. |
is_deleted boolean | True if this object has been deleted. |
created_at string (date-time) | Creation time for this object. |
metadata metadata | Key-value metadata |
Example
{
"id": "site_3merk33gt21kym11een1",
"name": "string",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"geo": {
"location": {
"lat": 41.290485,
"lng": 2.1829076
},
"radius": 100
},
"map": {
"location": {
"lat": 41.290485,
"lng": 2.1829076
},
"place_id": "string",
"address": "string",
"image_url": "string"
},
"phone": "string",
"email": "string",
"info": "string",
"timezone": "Europe/Madrid",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
List sites
Cursor value obtained from a previous request to the same endpoint. If not present, the first page of results is returned.
Items per page.
Sort order. Field name, followed by :asc
or :desc
. Defaults to id:desc
. In most models, IDs are assigned in increasing order in time, so sorting by id
is equivalent to sorting by created_at
. Depending on the model, sorting on some fields might not be supported.
data site | |
has_next boolean | |
cursor_next string (cursor) |
Response Example (200 OK)
{
"data": [
{
"id": "site_3merk33gt21kym11een1",
"name": "string",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"geo": {
"location": {
"lat": 41.290485,
"lng": 2.1829076
},
"radius": 100
},
"map": {
"location": {
"lat": 41.290485,
"lng": 2.1829076
},
"place_id": "string",
"address": "string",
"image_url": "string"
},
"phone": "string",
"email": "string",
"info": "string",
"timezone": "Europe/Madrid",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
],
"has_next": true,
"cursor_next": "i9vmCnOgONT2AjUMCn1K1N5cJg=="
}
Get site
site ID
Response Example (200 OK)
{
"id": "site_3merk33gt21kym11een1",
"name": "string",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"geo": {
"location": {
"lat": 41.290485,
"lng": 2.1829076
},
"radius": 100
},
"map": {
"location": {
"lat": 41.290485,
"lng": 2.1829076
},
"place_id": "string",
"address": "string",
"image_url": "string"
},
"phone": "string",
"email": "string",
"info": "string",
"timezone": "Europe/Madrid",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Edit site
site ID
metadata metadata | Key-value metadata |
Request Example
{
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Response Example (200 OK)
{
"id": "site_3merk33gt21kym11een1",
"name": "string",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"geo": {
"location": {
"lat": 41.290485,
"lng": 2.1829076
},
"radius": 100
},
"map": {
"location": {
"lat": 41.290485,
"lng": 2.1829076
},
"place_id": "string",
"address": "string",
"image_url": "string"
},
"phone": "string",
"email": "string",
"info": "string",
"timezone": "Europe/Madrid",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Products
product
id string | product ID |
name string | Human-friendly product name. Show this to the user instead of the product ID! |
Example
{
"id": "controller_ethernet",
"name": "Controller Ethernet"
}
List products
Cursor value obtained from a previous request to the same endpoint. If not present, the first page of results is returned.
Items per page.
Sort order. Field name, followed by :asc
or :desc
. Defaults to id:desc
. In most models, IDs are assigned in increasing order in time, so sorting by id
is equivalent to sorting by created_at
. Depending on the model, sorting on some fields might not be supported.
data product | |
has_next boolean | |
cursor_next string (cursor) |
Response Example (200 OK)
{
"data": [
{
"id": "controller_ethernet",
"name": "Controller Ethernet"
}
],
"has_next": true,
"cursor_next": "i9vmCnOgONT2AjUMCn1K1N5cJg=="
}
Get product
product ID
Response Example (200 OK)
{
"id": "controller_ethernet",
"name": "Controller Ethernet"
}
Devices
Physical Akiles device installed in the organization.
Each device has a set of capabilities which depend on the product.
The device
object also contains an overview of the current device status (online/offline, battery level).
If hardware_id = null
, the device is "virtual". It is designed for API testing: it behaves as close as possible to a real device in API calls but is not backed by a real hardware device. Gadget actions always succeed.
device
id string (device_id) | device ID |
created_at string (date-time) | Creation time for this object. |
name string | Device name |
organization_id string (organization_id) | organization ID |
site_id string (site_id) | site ID |
hardware_id string (hardware_id) | Hardware ID. This is the serial number printed in the device's label, as a QR and in some products as text. |
metadata metadata | Key-value metadata |
product_id string | Product ID. NOTE: do not show this to the user directly, it's not capitalized properly. Use the |
revision_id string | Hardware revision ID. |
input_rules device_input_rule | Input rule array. Input rules configure a device to remotely trigger gadget actions on other devices remotely when the user inputs a PIN or an NFC card. Must be empty if |
capabilities device_capabilities | Device capabilities. |
status device_status | Device status. |
is_deleted boolean | True if this object has been deleted. |
Example
{
"id": "dev_3merk33gt3l525ryhcmh",
"created_at": "2024-03-13T16:56:51.766836837Z",
"name": "string",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"site_id": "site_3merk33gt21kym11een1",
"hardware_id": "hw_3merk33gt2a7grgmljhh",
"metadata": {
"key1": "value1",
"key2": "value2"
},
"product_id": "controller_ethernet",
"revision_id": "06",
"input_rules": [
{
"code": "*2",
"gadget_id": "gad_3merk33gt1hnl6pvbu71",
"action_id": "open"
}
],
"capabilities": {
"gadgets": "multiple",
"gadget_max_count": 6,
"script": true,
"internet": true,
"ethernet": true,
"wifi": false,
"cellular": false,
"pin": false,
"nfc": false,
"input_rules": false,
"mains": true,
"battery": true,
"battery_rechargeable": true
},
"status": {
"online": true,
"mains_present": true,
"battery_present": true,
"battery_charging": true,
"battery_percent": 87.2
},
"is_deleted": false
}
device_capabilities
Device capabilities.
gadgets string none, single, multiple | What level of gadget support this device has. |
gadget_max_count integer | Maximum amount of gadgets the device supports. Will always be |
script boolean | Whether this device supports gadgets with custom scripts. |
internet boolean | Whether this device supports some form of internet connection, which means it can connect to Akiles Cloud directly instead of through gateways. |
ethernet boolean | Whether this device supports Ethernet. |
wifi boolean | Whether this device supports WiFi. |
cellular boolean | Whether this device supports cellular internet. |
pin boolean | Whether this device supports PIN entry (i.e. has a keypad). |
nfc boolean | Whether this device supports NFC (either physical cards, or emulated cards in a phone) |
input_rules boolean | Whether this device supports input rules. |
mains boolean | Whether this device supports being powered from mains (either directly or with an AC adapter). |
battery boolean | Whether this device supports being powered by batteries (rechargeable or not). It is possible for both |
battery_rechargeable boolean | If true, this device contains a rechargeable battery, and can recharge it from mains power. |
Example
{
"gadgets": "multiple",
"gadget_max_count": 6,
"script": true,
"internet": true,
"ethernet": true,
"wifi": false,
"cellular": false,
"pin": false,
"nfc": false,
"input_rules": false,
"mains": true,
"battery": true,
"battery_rechargeable": true
}
device_status
Device status.
online boolean | True if this device is online (meaning it's on and has a working connection to Akiles Cloud, either directly or through gateways). |
mains_present boolean | True if this device currently has mains power. |
battery_present boolean | True if this device currently has a battery inserted. |
battery_charging boolean | True if this device currently is charging the battery from mains power. |
battery_percent number | Battery percentage, 0-100. |
Example
{
"online": true,
"mains_present": true,
"battery_present": true,
"battery_charging": true,
"battery_percent": 87.2
}
device_input_rule
Input rules allow configuring "codes" that act as prefixes for PINs or cards. This allows a single device to
- Trigger different gadgets (local or remote).
- Trigger different actions in a gadget.
- Mix local and remote gadgets. One input rule can point to a local gadget (in this device), and another to a remote gadget (in another device, will be triggered via AkilesNet).
Example:
"input_rules": [
{
"code": "1",
"gadget_id": "gad_3z4f8pubhdcj2fvfmkxh", // street
"action_id": "open"
},
{
"code": "2",
"gadget_id": "gad_3z4f8s623va4l9nea7u1", // garage door
"action_id": "up"
},
{
"code": "3",
"gadget_id": "gad_3z4f8s623va4l9nea7u1", // garage door
"action_id": "down"
},
{
"code": "4",
"gadget_id": "gad_3z4f8s623va4l9nea7u1", // garage door
"action_id": "stop"
}
]
Typing "1" and your PIN, or typing "1" and tapping your card/phone would open the street door. Same with "2" would move the garage door up, "3" down, etc.
Keypad rule codes accept digits 0-9, hash #
and star *
.
You can define one keypad rule as the "default" one by setting the code to ""
. This is the rule that will be triggered if you type a PIN or tap a card directly without typing anything before. A default rule can coexist with other rules at the same time, but then all the other rules can't start with a digit (only with *
or #
), because it'd otherwise be ambiguous whether the user is typing a rule code or a PIN.
If no input rules are defined (input_rules
is an empty array), pins/cards will open the first action of the first gadget in the current device.
code string | Keypad code prefix. String consisting of digits 0-9, pound ( |
gadget_id string (gadget_id) | Destination gadget ID. It can be a gadget on this device, or on another device. |
action_id string | Destination action ID. It must be a valid action in the destination gadget. |
Example
{
"code": "*2",
"gadget_id": "gad_3merk33gt1hnl6pvbu71",
"action_id": "open"
}
List devices
Cursor value obtained from a previous request to the same endpoint. If not present, the first page of results is returned.
Items per page.
Sort order. Field name, followed by :asc
or :desc
. Defaults to id:desc
. In most models, IDs are assigned in increasing order in time, so sorting by id
is equivalent to sorting by created_at
. Depending on the model, sorting on some fields might not be supported.
data device | |
has_next boolean | |
cursor_next string (cursor) |
Response Example (200 OK)
{
"data": [
{
"id": "dev_3merk33gt3l525ryhcmh",
"created_at": "2024-03-13T16:56:51.766836837Z",
"name": "string",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"site_id": "site_3merk33gt21kym11een1",
"hardware_id": "hw_3merk33gt2a7grgmljhh",
"metadata": {
"key1": "value1",
"key2": "value2"
},
"product_id": "controller_ethernet",
"revision_id": "06",
"input_rules": [
{
"code": "*2",
"gadget_id": "gad_3merk33gt1hnl6pvbu71",
"action_id": "open"
}
],
"capabilities": {
"gadgets": "multiple",
"gadget_max_count": 6,
"script": true,
"internet": true,
"ethernet": true,
"wifi": false,
"cellular": false,
"pin": false,
"nfc": false,
"input_rules": false,
"mains": true,
"battery": true,
"battery_rechargeable": true
},
"status": {
"online": true,
"mains_present": true,
"battery_present": true,
"battery_charging": true,
"battery_percent": 87.2
},
"is_deleted": false
}
],
"has_next": true,
"cursor_next": "i9vmCnOgONT2AjUMCn1K1N5cJg=="
}
Get device
device ID
Response Example (200 OK)
{
"id": "dev_3merk33gt3l525ryhcmh",
"created_at": "2024-03-13T16:56:51.766836837Z",
"name": "string",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"site_id": "site_3merk33gt21kym11een1",
"hardware_id": "hw_3merk33gt2a7grgmljhh",
"metadata": {
"key1": "value1",
"key2": "value2"
},
"product_id": "controller_ethernet",
"revision_id": "06",
"input_rules": [
{
"code": "*2",
"gadget_id": "gad_3merk33gt1hnl6pvbu71",
"action_id": "open"
}
],
"capabilities": {
"gadgets": "multiple",
"gadget_max_count": 6,
"script": true,
"internet": true,
"ethernet": true,
"wifi": false,
"cellular": false,
"pin": false,
"nfc": false,
"input_rules": false,
"mains": true,
"battery": true,
"battery_rechargeable": true
},
"status": {
"online": true,
"mains_present": true,
"battery_present": true,
"battery_charging": true,
"battery_percent": 87.2
},
"is_deleted": false
}
Edit device
device ID
metadata metadata | Key-value metadata |
Request Example
{
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Response Example (200 OK)
{
"id": "dev_3merk33gt3l525ryhcmh",
"created_at": "2024-03-13T16:56:51.766836837Z",
"name": "string",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"site_id": "site_3merk33gt21kym11een1",
"hardware_id": "hw_3merk33gt2a7grgmljhh",
"metadata": {
"key1": "value1",
"key2": "value2"
},
"product_id": "controller_ethernet",
"revision_id": "06",
"input_rules": [
{
"code": "*2",
"gadget_id": "gad_3merk33gt1hnl6pvbu71",
"action_id": "open"
}
],
"capabilities": {
"gadgets": "multiple",
"gadget_max_count": 6,
"script": true,
"internet": true,
"ethernet": true,
"wifi": false,
"cellular": false,
"pin": false,
"nfc": false,
"input_rules": false,
"mains": true,
"battery": true,
"battery_rechargeable": true
},
"status": {
"online": true,
"mains_present": true,
"battery_present": true,
"battery_charging": true,
"battery_percent": 87.2
},
"is_deleted": false
}
Gadgets
A gadget is a "thing that can be controlled" by the user. Gadgets can be doors, lights, heating, window blinds...
Gadgets are NOT physical devices. Some devices can have multiple gadgets because they have multiple inputs/outputs so they can control multiple things (Controllers). Others can have only one (the Cylinder and the Roomlock, for example). Others can have none (the Gateway Ethernet, for example.
Gadgets can have multiple actions depending on what they are. For example, a normal door will just have an open
action, but a blind\ncan have raise
and lower
actions, and a light can have on
and off
actions. Actions are identified by their string ID, and they\nalso have a human-friendly name for display in the UI.
gadget_action
id string | action ID |
name string | Action user-friendly name |
Example
{
"id": "open",
"name": "Open"
}
gadget
id string (gadget_id) | gadget ID |
organization_id string (organization_id) | organization ID |
site_id string (site_id) | site ID |
device_id string (device_id) | device ID |
name string | Gadget name |
actions gadget_action | |
is_deleted boolean | True if this object has been deleted. |
created_at string (date-time) | Creation time for this object. |
metadata metadata | Key-value metadata |
Example
{
"id": "gad_3merk33gt1hnl6pvbu71",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"site_id": "site_3merk33gt21kym11een1",
"device_id": "dev_3merk33gt3l525ryhcmh",
"name": "string",
"actions": [
{
"id": "open",
"name": "Open"
}
],
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
List gadgets
Cursor value obtained from a previous request to the same endpoint. If not present, the first page of results is returned.
Items per page.
Sort order. Field name, followed by :asc
or :desc
. Defaults to id:desc
. In most models, IDs are assigned in increasing order in time, so sorting by id
is equivalent to sorting by created_at
. Depending on the model, sorting on some fields might not be supported.
data gadget | |
has_next boolean | |
cursor_next string (cursor) |
Response Example (200 OK)
{
"data": [
{
"id": "gad_3merk33gt1hnl6pvbu71",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"site_id": "site_3merk33gt21kym11een1",
"device_id": "dev_3merk33gt3l525ryhcmh",
"name": "string",
"actions": [
{
"id": "open",
"name": "Open"
}
],
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
],
"has_next": true,
"cursor_next": "i9vmCnOgONT2AjUMCn1K1N5cJg=="
}
Get gadget
gadget ID
Response Example (200 OK)
{
"id": "gad_3merk33gt1hnl6pvbu71",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"site_id": "site_3merk33gt21kym11een1",
"device_id": "dev_3merk33gt3l525ryhcmh",
"name": "string",
"actions": [
{
"id": "open",
"name": "Open"
}
],
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Edit gadget
gadget ID
metadata metadata | Key-value metadata |
Request Example
{
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Response Example (200 OK)
{
"id": "gad_3merk33gt1hnl6pvbu71",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"site_id": "site_3merk33gt21kym11een1",
"device_id": "dev_3merk33gt3l525ryhcmh",
"name": "string",
"actions": [
{
"id": "open",
"name": "Open"
}
],
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Do gadget action
This endpoint is only available to Akiles customers that have the whitelabel integration feature which has extra cost. If you want to make your integration public we recommend using the non whitelabel option.
gadget ID
gadget action ID
Request Example
{}
Response Example (200 OK)
{}
Members
member
id string (member_id) | member ID |
organization_id string (organization_id) | organization ID |
name string | Member name shown in the admin panel. This is for the organization admins to identify the member, it's never shown to the user. |
starts_at string (date-time) | Start date of the member's access. If null, the access is valid immediately. |
ends_at string (date-time) | End date of the member's access. If null, the access is valid forever (ie, until an end date is set or the member is deleted.) |
is_deleted boolean | True if this object has been deleted. |
created_at string (date-time) | Creation time for this object. |
metadata metadata | Key-value metadata |
Example
{
"id": "mem_3merk33gt7ml3tde71f3",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"name": "John Doe",
"starts_at": "2024-03-13T16:56:51.766836837Z",
"ends_at": "2024-03-13T16:56:51.766836837Z",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
member_email
id string (member_email_id) | member email ID |
organization_id string (organization_id) | organization ID |
member_id string (member_id) | member ID |
email string (email_address) | Email address |
is_deleted boolean | True if this object has been deleted. |
created_at string (date-time) | Creation time for this object. |
metadata metadata | Key-value metadata |
Example
{
"id": "me_3rkd7ya2pnjysjqbluj1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"email": "hello@example.com",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
member_magic_link
id string (member_magic_link_id) | member magic link ID |
organization_id string (organization_id) | organization ID |
member_id string (member_id) | member ID |
is_deleted boolean | True if this object has been deleted. |
created_at string (date-time) | Creation time for this object. |
metadata metadata | Key-value metadata |
Example
{
"id": "mml_3rkd2f2mv925ptvpgblh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
member_magic_link_revealed
link string (url) | Magic link for this member. The guest will be able to use Akiles instantly by clicking it. |
id string (member_magic_link_id) | member magic link ID |
organization_id string (organization_id) | organization ID |
member_id string (member_id) | member ID |
is_deleted boolean | True if this object has been deleted. |
created_at string (date-time) | Creation time for this object. |
metadata metadata | Key-value metadata |
Example
{
"link": "https://link.akiles.app/#ml_8502850982345_f00i1nslz20gkm",
"id": "mml_3rkd2f2mv925ptvpgblh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
member_pin
id string (member_pin_id) | member pin ID |
organization_id string (organization_id) | organization ID |
member_id string (member_id) | member ID |
length integer | Pin length |
is_deleted boolean | True if this object has been deleted. |
created_at string (date-time) | Creation time for this object. |
metadata metadata | Key-value metadata |
Example
{
"id": "mp_3rkd2fmgmmdh1dgkvluh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"length": 6,
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
member_pin_revealed
pin string | Pin number. |
id string (member_pin_id) | member pin ID |
organization_id string (organization_id) | organization ID |
member_id string (member_id) | member ID |
length integer | Pin length |
is_deleted boolean | True if this object has been deleted. |
created_at string (date-time) | Creation time for this object. |
metadata metadata | Key-value metadata |
Example
{
"pin": "123456",
"id": "mp_3rkd2fmgmmdh1dgkvluh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"length": 6,
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
member_card
id string (member_card_id) | member card ID |
organization_id string (organization_id) | organization ID |
member_id string (member_id) | member ID |
card_id string (card_id) | card ID |
is_deleted boolean | True if this object has been deleted. |
created_at string (date-time) | Creation time for this object. |
metadata metadata | Key-value metadata |
Example
{
"id": "mc_3yd55tbrelfxhjjvkdqh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"card_id": "crd_3ykdnnld7d57x83eqcm1",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
card
id string (card_id) | Card ID |
uid string | ISO14443 UID for the card. Always 4, 7 or 10 bytes, encoded in hex. |
printed_code string | Unique code physically printed on the card. |
created_at string (date-time) | Creation time for this object. |
Example
{
"id": "crd_3ykdnnld7d57x83eqcm1",
"uid": "041E53F2FF6780",
"printed_code": "WHJSA",
"created_at": "2024-03-13T16:56:51.766836837Z"
}
member_group_association
id string (member_group_association_id) | member group association ID |
organization_id string (organization_id) | organization ID |
member_id string (member_id) | member ID |
member_group_id string (member_group_id) | member_group ID |
starts_at string (date-time) | Start date of the member's access. If null, the access is valid immediately. |
ends_at string (date-time) | End date of the member's access. If null, the access is valid forever (ie, until an end date is set or the member is deleted.) |
is_deleted boolean | True if this object has been deleted. |
created_at string (date-time) | Creation time for this object. |
metadata metadata | Key-value metadata |
Example
{
"id": "mga_3rkd81x2hc3qluv56pl1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"member_group_id": "mg_3merk33gt1692dk2p2m1",
"starts_at": "2024-03-13T16:56:51.766836837Z",
"ends_at": "2024-03-13T16:56:51.766836837Z",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
List members
Cursor value obtained from a previous request to the same endpoint. If not present, the first page of results is returned.
Items per page.
Sort order. Field name, followed by :asc
or :desc
. Defaults to id:desc
. In most models, IDs are assigned in increasing order in time, so sorting by id
is equivalent to sorting by created_at
. Depending on the model, sorting on some fields might not be supported.
Filter by deletion status
Filter by email address
Filter by metadata.source
Filter by metadata.sourceID
data member | |
has_next boolean | |
cursor_next string (cursor) |
Response Example (200 OK)
{
"data": [
{
"id": "mem_3merk33gt7ml3tde71f3",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"name": "John Doe",
"starts_at": "2024-03-13T16:56:51.766836837Z",
"ends_at": "2024-03-13T16:56:51.766836837Z",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
],
"has_next": true,
"cursor_next": "i9vmCnOgONT2AjUMCn1K1N5cJg=="
}
Create member
name string | Member name shown in the admin panel. This is for the organization admins to identify the member, it's never shown to the user. |
starts_at string (date-time) | Start date of the member's access. If null, the access is valid immediately. |
ends_at string (date-time) | End date of the member's access. If null, the access is valid forever (ie, until an end date is set or the member is deleted.) |
metadata metadata | Key-value metadata |
Request Example
{
"name": "John Doe",
"starts_at": "2024-03-13T16:56:51.766836837Z",
"ends_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Response Example (200 OK)
{
"id": "mem_3merk33gt7ml3tde71f3",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"name": "John Doe",
"starts_at": "2024-03-13T16:56:51.766836837Z",
"ends_at": "2024-03-13T16:56:51.766836837Z",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Get member
member ID
Response Example (200 OK)
{
"id": "mem_3merk33gt7ml3tde71f3",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"name": "John Doe",
"starts_at": "2024-03-13T16:56:51.766836837Z",
"ends_at": "2024-03-13T16:56:51.766836837Z",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Edit member
member ID
name string | Member name shown in the admin panel. This is for the organization admins to identify the member, it's never shown to the user. |
starts_at string (date-time) | Start date of the member's access. If null, the access is valid immediately. |
ends_at string (date-time) | End date of the member's access. If null, the access is valid forever (ie, until an end date is set or the member is deleted.) |
is_deleted boolean | True if this object has been deleted. |
metadata metadata | Key-value metadata |
Request Example
{
"name": "John Doe",
"starts_at": "2024-03-13T16:56:51.766836837Z",
"ends_at": "2024-03-13T16:56:51.766836837Z",
"is_deleted": false,
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Response Example (200 OK)
{
"id": "mem_3merk33gt7ml3tde71f3",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"name": "John Doe",
"starts_at": "2024-03-13T16:56:51.766836837Z",
"ends_at": "2024-03-13T16:56:51.766836837Z",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Delete member
member ID
Response Example (200 OK)
{
"id": "mem_3merk33gt7ml3tde71f3",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"name": "John Doe",
"starts_at": "2024-03-13T16:56:51.766836837Z",
"ends_at": "2024-03-13T16:56:51.766836837Z",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
List emails
Cursor value obtained from a previous request to the same endpoint. If not present, the first page of results is returned.
Items per page.
Search parameter
Sort order. Field name, followed by :asc
or :desc
. Defaults to id:desc
. In most models, IDs are assigned in increasing order in time, so sorting by id
is equivalent to sorting by created_at
. Depending on the model, sorting on some fields might not be supported.
member ID
data member_email | |
has_next boolean | |
cursor_next string (cursor) |
Response Example (200 OK)
{
"data": [
{
"id": "me_3rkd7ya2pnjysjqbluj1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"email": "hello@example.com",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
],
"has_next": true,
"cursor_next": "i9vmCnOgONT2AjUMCn1K1N5cJg=="
}
Create email
member ID
email string (email) | Email address |
metadata metadata | Key-value metadata |
Request Example
{
"email": "string (email)",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Response Example (200 OK)
{
"id": "me_3rkd7ya2pnjysjqbluj1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"email": "hello@example.com",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Get email
member ID
member email ID
Response Example (200 OK)
{
"id": "me_3rkd7ya2pnjysjqbluj1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"email": "hello@example.com",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Edit email
member ID
member email ID
metadata metadata | Key-value metadata |
Request Example
{
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Response Example (200 OK)
{
"id": "me_3rkd7ya2pnjysjqbluj1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"email": "hello@example.com",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Delete email
member ID
member email ID
Response Example (200 OK)
{
"id": "me_3rkd7ya2pnjysjqbluj1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"email": "hello@example.com",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
List magic links
Cursor value obtained from a previous request to the same endpoint. If not present, the first page of results is returned.
Items per page.
Sort order. Field name, followed by :asc
or :desc
. Defaults to id:desc
. In most models, IDs are assigned in increasing order in time, so sorting by id
is equivalent to sorting by created_at
. Depending on the model, sorting on some fields might not be supported.
member ID
data member_magic_link | |
has_next boolean | |
cursor_next string (cursor) |
Response Example (200 OK)
{
"data": [
{
"id": "mml_3rkd2f2mv925ptvpgblh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
],
"has_next": true,
"cursor_next": "i9vmCnOgONT2AjUMCn1K1N5cJg=="
}
Create magic link
member ID
metadata metadata | Key-value metadata |
Request Example
{
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Response Example (200 OK)
{
"link": "https://link.akiles.app/#ml_8502850982345_f00i1nslz20gkm",
"id": "mml_3rkd2f2mv925ptvpgblh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Get magic link
member ID
member magic link ID
Response Example (200 OK)
{
"id": "mml_3rkd2f2mv925ptvpgblh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Edit magic link
member ID
member magic link ID
metadata metadata | Key-value metadata |
Request Example
{
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Response Example (200 OK)
{
"id": "mml_3rkd2f2mv925ptvpgblh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Delete magic link
member ID
member magic link ID
Response Example (200 OK)
{
"id": "mml_3rkd2f2mv925ptvpgblh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Reveal magic link
member ID
member magic link ID
Request Example
{}
Response Example (200 OK)
{
"link": "https://link.akiles.app/#ml_8502850982345_f00i1nslz20gkm",
"id": "mml_3rkd2f2mv925ptvpgblh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
List pins
Cursor value obtained from a previous request to the same endpoint. If not present, the first page of results is returned.
Items per page.
Sort order. Field name, followed by :asc
or :desc
. Defaults to id:desc
. In most models, IDs are assigned in increasing order in time, so sorting by id
is equivalent to sorting by created_at
. Depending on the model, sorting on some fields might not be supported.
member ID
data member_pin | |
has_next boolean | |
cursor_next string (cursor) |
Response Example (200 OK)
{
"data": [
{
"id": "mp_3rkd2fmgmmdh1dgkvluh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"length": 6,
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
],
"has_next": true,
"cursor_next": "i9vmCnOgONT2AjUMCn1K1N5cJg=="
}
Create pin
When creating a PIN, you can specify it in 3 ways depending on the fields you send in the request:
- No fields: Akiles generated a random PIN, of the length configured in the organization's settings.
length
only: Akiles generated a random PIN of the length you specify.pin
only: The PIN is the one you specify.
The generated random PIN is included in the response.
It is not allowed to have two (non-deleted) members with the same PIN in a given organization. For this reason, we recommend you let Akiles generate a random PIN instead of generating one yourself if you can (Akiles generates a PIN that is different to all existing members). If you specify the PIN directly, you have to ensure there's no PIN collisions yourself.
member ID
length integer | Pin length. Optional (you can either not specify it, or set it to |
pin string | Pin number. Optional (you can either not specify it, or set it to |
metadata metadata | Key-value metadata |
Request Example
{
"length": 6,
"pin": "123456",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Response Example (200 OK)
{
"pin": "123456",
"id": "mp_3rkd2fmgmmdh1dgkvluh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"length": 6,
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Get pin
member ID
member pin ID
Response Example (200 OK)
{
"id": "mp_3rkd2fmgmmdh1dgkvluh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"length": 6,
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Edit pin
NOTE: It is not possible to edit a MemberPin to change a PIN. Instead, delete it and create a new one.
member ID
member pin ID
metadata metadata | Key-value metadata |
Request Example
{
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Response Example (200 OK)
{
"id": "mp_3rkd2fmgmmdh1dgkvluh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"length": 6,
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Delete pin
member ID
member pin ID
Response Example (200 OK)
{
"id": "mp_3rkd2fmgmmdh1dgkvluh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"length": 6,
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Reveal pin
member ID
member pin ID
Request Example
{}
Response Example (200 OK)
{
"pin": "123456",
"id": "mp_3rkd2fmgmmdh1dgkvluh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"length": 6,
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
List cards
Cursor value obtained from a previous request to the same endpoint. If not present, the first page of results is returned.
Items per page.
Sort order. Field name, followed by :asc
or :desc
. Defaults to id:desc
. In most models, IDs are assigned in increasing order in time, so sorting by id
is equivalent to sorting by created_at
. Depending on the model, sorting on some fields might not be supported.
member ID
data member_card | |
has_next boolean | |
cursor_next string (cursor) |
Response Example (200 OK)
{
"data": [
{
"id": "mc_3yd55tbrelfxhjjvkdqh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"card_id": "crd_3ykdnnld7d57x83eqcm1",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
],
"has_next": true,
"cursor_next": "i9vmCnOgONT2AjUMCn1K1N5cJg=="
}
Create card
member ID
card_id string (card_id) | Card ID. Optional. One of |
printed_code string | Unique code physically printed on the card. Optional. One of |
metadata metadata | Key-value metadata |
Request Example
{
"card_id": "crd_3ykdnnld7d57x83eqcm1",
"printed_code": "WHJSA",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Response Example (200 OK)
{
"id": "mc_3yd55tbrelfxhjjvkdqh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"card_id": "crd_3ykdnnld7d57x83eqcm1",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Get card
member ID
member card ID
Response Example (200 OK)
{
"id": "mc_3yd55tbrelfxhjjvkdqh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"card_id": "crd_3ykdnnld7d57x83eqcm1",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Edit card
member ID
member card ID
metadata metadata | Key-value metadata |
Request Example
{
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Response Example (200 OK)
{
"id": "mc_3yd55tbrelfxhjjvkdqh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"card_id": "crd_3ykdnnld7d57x83eqcm1",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Delete card
member ID
member card ID
Response Example (200 OK)
{
"id": "mc_3yd55tbrelfxhjjvkdqh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"card_id": "crd_3ykdnnld7d57x83eqcm1",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
List group associations
Cursor value obtained from a previous request to the same endpoint. If not present, the first page of results is returned.
Items per page.
Sort order. Field name, followed by :asc
or :desc
. Defaults to id:desc
. In most models, IDs are assigned in increasing order in time, so sorting by id
is equivalent to sorting by created_at
. Depending on the model, sorting on some fields might not be supported.
member ID
data member_group_association | |
has_next boolean | |
cursor_next string (cursor) |
Response Example (200 OK)
{
"data": [
{
"id": "mga_3rkd81x2hc3qluv56pl1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"member_group_id": "mg_3merk33gt1692dk2p2m1",
"starts_at": "2024-03-13T16:56:51.766836837Z",
"ends_at": "2024-03-13T16:56:51.766836837Z",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
],
"has_next": true,
"cursor_next": "i9vmCnOgONT2AjUMCn1K1N5cJg=="
}
Create group association
member ID
member_group_id string (member_group_id) | member_group ID |
starts_at string (date-time) | Start date of the member's access. If null, the access is valid immediately. |
ends_at string (date-time) | End date of the member's access. If null, the access is valid forever (ie, until an end date is set or the member is deleted.) |
metadata metadata | Key-value metadata |
Request Example
{
"member_group_id": "mg_3merk33gt1692dk2p2m1",
"starts_at": "2024-03-13T16:56:51.766836837Z",
"ends_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Response Example (200 OK)
{
"id": "mga_3rkd81x2hc3qluv56pl1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"member_group_id": "mg_3merk33gt1692dk2p2m1",
"starts_at": "2024-03-13T16:56:51.766836837Z",
"ends_at": "2024-03-13T16:56:51.766836837Z",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Get group association
member ID
member group association ID
Response Example (200 OK)
{
"id": "mga_3rkd81x2hc3qluv56pl1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"member_group_id": "mg_3merk33gt1692dk2p2m1",
"starts_at": "2024-03-13T16:56:51.766836837Z",
"ends_at": "2024-03-13T16:56:51.766836837Z",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Edit group association
member ID
member group association ID
starts_at string (date-time) | Start date of the member's access. If null, the access is valid immediately. |
ends_at string (date-time) | End date of the member's access. If null, the access is valid forever (ie, until an end date is set or the member is deleted.) |
metadata metadata | Key-value metadata |
Request Example
{
"starts_at": "2024-03-13T16:56:51.766836837Z",
"ends_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Response Example (200 OK)
{
"id": "mga_3rkd81x2hc3qluv56pl1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"member_group_id": "mg_3merk33gt1692dk2p2m1",
"starts_at": "2024-03-13T16:56:51.766836837Z",
"ends_at": "2024-03-13T16:56:51.766836837Z",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Delete group association
member ID
member group association ID
Response Example (200 OK)
{
"id": "mga_3rkd81x2hc3qluv56pl1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"member_id": "mem_3merk33gt7ml3tde71f3",
"member_group_id": "mg_3merk33gt1692dk2p2m1",
"starts_at": "2024-03-13T16:56:51.766836837Z",
"ends_at": "2024-03-13T16:56:51.766836837Z",
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Member groups
member_group
id string (member_group_id) | member_group ID |
organization_id string (organization_id) | organization ID |
name string | Group name |
permissions member_group_permission_rule | Permission rules granted to members of this group. A member can have multiple groups, and the permissions are the union of all the groups. Access is granted if at least one rule matches. |
is_deleted boolean | True if this object has been deleted. |
created_at string (date-time) | Creation time for this object. |
metadata metadata | Key-value metadata |
Example
{
"id": "mg_3merk33gt1692dk2p2m1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"name": "string",
"permissions": [
{
"site_id": "site_3merk33gt21kym11een1",
"gadget_id": "gad_3merk33gt1hnl6pvbu71",
"action_id": "string",
"schedule_id": "sch_3merk33gt21kyz1f3z1",
"presence": "string"
}
],
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
member_group_permission_rule
Rule granting permissions to a member group. Examples:
{}
matches all gadgets in all sites{"site_id": "site_3merk33gt21kym11een1"}
matches all gadgets in that site.{"gadget_id": "gad_3merk33gt1hnl6pvbu71"}
matches that gadget only.
It is not allowed to set site_id
and gadget_id
at the same time.
site_id string (site_id) | Site ID. If set, the rule only matches gadgets in this site. |
gadget_id string (gadget_id) | Gadget ID. If set, the rule only matches this gadget. |
action_id string | Action ID. If set, the rule only matches this particular action within the gadget. Only one action can be chosen. If you want to allow multiple actions in the same gadget, create one permission rule for each action. If you set this, |
schedule_id string (schedule_id) | Schedule ID. If set, the rule only matches if the current time is within the schedule. |
presence string none, gps | Presence check performed.
Actions via PINs, cards and Bluetooth are always allowed and not affected by this setting. |
Example
{
"site_id": "site_3merk33gt21kym11een1",
"gadget_id": "gad_3merk33gt1hnl6pvbu71",
"action_id": "string",
"schedule_id": "sch_3merk33gt21kyz1f3z1",
"presence": "string"
}
List member groups
Cursor value obtained from a previous request to the same endpoint. If not present, the first page of results is returned.
Items per page.
Search parameter
Sort order. Field name, followed by :asc
or :desc
. Defaults to id:desc
. In most models, IDs are assigned in increasing order in time, so sorting by id
is equivalent to sorting by created_at
. Depending on the model, sorting on some fields might not be supported.
data member_group | |
has_next boolean | |
cursor_next string (cursor) |
Response Example (200 OK)
{
"data": [
{
"id": "mg_3merk33gt1692dk2p2m1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"name": "string",
"permissions": [
{
"site_id": "site_3merk33gt21kym11een1",
"gadget_id": "gad_3merk33gt1hnl6pvbu71",
"action_id": "string",
"schedule_id": "sch_3merk33gt21kyz1f3z1",
"presence": "string"
}
],
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
],
"has_next": true,
"cursor_next": "i9vmCnOgONT2AjUMCn1K1N5cJg=="
}
Create member group
name string | Group name |
permissions member_group_permission_rule | Permission rules granted to members of this group. A member can have multiple groups, and the permissions are the union of all the groups. Access is granted if at least one rule matches. |
metadata metadata | Key-value metadata |
Request Example
{
"name": "string",
"permissions": [
{
"site_id": "site_3merk33gt21kym11een1",
"gadget_id": "gad_3merk33gt1hnl6pvbu71",
"action_id": "string",
"schedule_id": "sch_3merk33gt21kyz1f3z1",
"presence": "string"
}
],
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Response Example (200 OK)
{
"id": "mg_3merk33gt1692dk2p2m1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"name": "string",
"permissions": [
{
"site_id": "site_3merk33gt21kym11een1",
"gadget_id": "gad_3merk33gt1hnl6pvbu71",
"action_id": "string",
"schedule_id": "sch_3merk33gt21kyz1f3z1",
"presence": "string"
}
],
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Get member group
member_group ID
Response Example (200 OK)
{
"id": "mg_3merk33gt1692dk2p2m1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"name": "string",
"permissions": [
{
"site_id": "site_3merk33gt21kym11een1",
"gadget_id": "gad_3merk33gt1hnl6pvbu71",
"action_id": "string",
"schedule_id": "sch_3merk33gt21kyz1f3z1",
"presence": "string"
}
],
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Edit member group
member_group ID
name string | Group name |
permissions member_group_permission_rule | Permission rules granted to members of this group. A member can have multiple groups, and the permissions are the union of all the groups. Access is granted if at least one rule matches. |
metadata metadata | Key-value metadata |
Request Example
{
"name": "string",
"permissions": [
{
"site_id": "site_3merk33gt21kym11een1",
"gadget_id": "gad_3merk33gt1hnl6pvbu71",
"action_id": "string",
"schedule_id": "sch_3merk33gt21kyz1f3z1",
"presence": "string"
}
],
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Response Example (200 OK)
{
"id": "mg_3merk33gt1692dk2p2m1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"name": "string",
"permissions": [
{
"site_id": "site_3merk33gt21kym11een1",
"gadget_id": "gad_3merk33gt1hnl6pvbu71",
"action_id": "string",
"schedule_id": "sch_3merk33gt21kyz1f3z1",
"presence": "string"
}
],
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Delete member group
member_group ID
Response Example (200 OK)
{
"id": "mg_3merk33gt1692dk2p2m1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"name": "string",
"permissions": [
{
"site_id": "site_3merk33gt21kym11een1",
"gadget_id": "gad_3merk33gt1hnl6pvbu71",
"action_id": "string",
"schedule_id": "sch_3merk33gt21kyz1f3z1",
"presence": "string"
}
],
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
Events
Everything that happens in an Akiles organization is logged as an Event.
Events are structured in "subject - verb - object" form:
- Subject: who did the action. It can be a member, an administrator, an API key, an OAuth application...
- Verb: what action was done on the object. Create, edit, delete, use...
- Object: what object the action was done on. A member, a gadget...
For example:
- gadget action events (i.e. someone opening a door) are reported with the member in the subject,
use
as verb, and the gadget action as object. - Member editions are reported with the admin user who did the action in the subject,
edit
as the verb, and the member as the object.
Offline delayed events
Gadget action events originate from the Akiles device when the user uses PINs, NFC cards or phone, or BT from a phone to open. If the Akiles device is offline (i.e. has no connection to Akiles Cloud), it buffers events in local persistent memory and reports them later when it becomes online again. When this happens, the event appears with the created_at
corresponding to the moment it originally happened, NOT the moment when it was reported.
event
id string (event_id) | event ID |
organization_id string (organization_id) | organization ID |
subject event_subject | |
verb string create, edit, delete, use | |
object event_object | |
created_at string (date-time) | Creation time for this object. |
Example
{
"id": "evt_3merk33gt21kym11een1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"subject": {
"member_id": "mem_3merk33gt7ml3tde71f3",
"member_email_id": "me_3rkd7ya2pnjysjqbluj1",
"member_magic_link_id": "mml_3rkd2f2mv925ptvpgblh",
"member_pin_id": "mp_3rkd2fmgmmdh1dgkvluh",
"member_card_id": "mc_3yd55tbrelfxhjjvkdqh",
"member_sdk_token_id": "mst_3yd55tbrelfxhjjvkdqh"
},
"verb": "use",
"object": {
"type": "gadget_action",
"device_id": "dev_3merk33gt3l525ryhcmh",
"gadget_id": "gad_3merk33gt1hnl6pvbu71",
"gadget_action_id": "open",
"member_id": "mem_3merk33gt7ml3tde71f3",
"member_email_id": "me_3rkd7ya2pnjysjqbluj1",
"member_magic_link_id": "mml_3rkd2f2mv925ptvpgblh",
"member_group_id": "mg_3merk33gt1692dk2p2m1",
"member_group_association_id": "mga_3rkd81x2hc3qluv56pl1",
"member_pin_id": "mp_3rkd2fmgmmdh1dgkvluh",
"member_card_id": "mc_3yd55tbrelfxhjjvkdqh",
"member_sdk_token_id": "mst_3yd55tbrelfxhjjvkdqh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"site_id": "site_3merk33gt21kym11een1",
"webhook_id": "wh_3merk33gt21kyz1f3z1",
"hardware_id": "hw_3merk33gt2a7grgmljhh",
"card_id": "crd_3ykdnnld7d57x83eqcm1"
},
"created_at": "2024-03-13T16:56:51.766836837Z"
}
event_subject
member_id string (member_id) | member ID |
member_email_id string (member_email_id) | member_email ID |
member_magic_link_id string (member_magic_link_id) | member_magic_link ID |
member_pin_id string (member_pin_id) | member_pin ID |
member_card_id string (member_card_id) | member_card ID |
member_sdk_token_id string (member_sdk_token_id) | member_sdk_token ID |
Example
{
"member_id": "mem_3merk33gt7ml3tde71f3",
"member_email_id": "me_3rkd7ya2pnjysjqbluj1",
"member_magic_link_id": "mml_3rkd2f2mv925ptvpgblh",
"member_pin_id": "mp_3rkd2fmgmmdh1dgkvluh",
"member_card_id": "mc_3yd55tbrelfxhjjvkdqh",
"member_sdk_token_id": "mst_3yd55tbrelfxhjjvkdqh"
}
event_object
type string device, gadget, gadget_action, member, member_email, member_magic_link, member_group, member_group_association, member_pin, member_card, member_sdk_token, organization, site, webhook, hardware, card | |
device_id string (device_id) | device ID |
gadget_id string (gadget_id) | gadget ID |
gadget_action_id string | gadget_action ID |
member_id string (member_id) | member ID |
member_email_id string (member_email_id) | member_email ID |
member_magic_link_id string (member_magic_link_id) | member_magic_link ID |
member_group_id string (member_group_id) | member_group ID |
member_group_association_id string (member_group_association_id) | member_group_association ID |
member_pin_id string (member_pin_id) | member_pin ID |
member_card_id string (member_card_id) | member_card ID |
member_sdk_token_id string (member_sdk_token_id) | member_sdk_token ID |
organization_id string (organization_id) | organization ID |
site_id string (site_id) | site ID |
webhook_id string (webhook_id) | webhook ID |
hardware_id string (hardware_id) | hardware ID |
card_id string (card_id) | card ID |
Example
{
"type": "gadget_action",
"device_id": "dev_3merk33gt3l525ryhcmh",
"gadget_id": "gad_3merk33gt1hnl6pvbu71",
"gadget_action_id": "open",
"member_id": "mem_3merk33gt7ml3tde71f3",
"member_email_id": "me_3rkd7ya2pnjysjqbluj1",
"member_magic_link_id": "mml_3rkd2f2mv925ptvpgblh",
"member_group_id": "mg_3merk33gt1692dk2p2m1",
"member_group_association_id": "mga_3rkd81x2hc3qluv56pl1",
"member_pin_id": "mp_3rkd2fmgmmdh1dgkvluh",
"member_card_id": "mc_3yd55tbrelfxhjjvkdqh",
"member_sdk_token_id": "mst_3yd55tbrelfxhjjvkdqh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"site_id": "site_3merk33gt21kym11een1",
"webhook_id": "wh_3merk33gt21kyz1f3z1",
"hardware_id": "hw_3merk33gt2a7grgmljhh",
"card_id": "crd_3ykdnnld7d57x83eqcm1"
}
List events
Cursor value obtained from a previous request to the same endpoint. If not present, the first page of results is returned.
Items per page.
Sort order. Field name, followed by :asc
or :desc
. Defaults to id:desc
. In most models, IDs are assigned in increasing order in time, so sorting by id
is equivalent to sorting by created_at
. Depending on the model, sorting on some fields might not be supported.
data event | |
has_next boolean | |
cursor_next string (cursor) |
Response Example (200 OK)
{
"data": [
{
"id": "evt_3merk33gt21kym11een1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"subject": {
"member_id": "mem_3merk33gt7ml3tde71f3",
"member_email_id": "me_3rkd7ya2pnjysjqbluj1",
"member_magic_link_id": "mml_3rkd2f2mv925ptvpgblh",
"member_pin_id": "mp_3rkd2fmgmmdh1dgkvluh",
"member_card_id": "mc_3yd55tbrelfxhjjvkdqh",
"member_sdk_token_id": "mst_3yd55tbrelfxhjjvkdqh"
},
"verb": "use",
"object": {
"type": "gadget_action",
"device_id": "dev_3merk33gt3l525ryhcmh",
"gadget_id": "gad_3merk33gt1hnl6pvbu71",
"gadget_action_id": "open",
"member_id": "mem_3merk33gt7ml3tde71f3",
"member_email_id": "me_3rkd7ya2pnjysjqbluj1",
"member_magic_link_id": "mml_3rkd2f2mv925ptvpgblh",
"member_group_id": "mg_3merk33gt1692dk2p2m1",
"member_group_association_id": "mga_3rkd81x2hc3qluv56pl1",
"member_pin_id": "mp_3rkd2fmgmmdh1dgkvluh",
"member_card_id": "mc_3yd55tbrelfxhjjvkdqh",
"member_sdk_token_id": "mst_3yd55tbrelfxhjjvkdqh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"site_id": "site_3merk33gt21kym11een1",
"webhook_id": "wh_3merk33gt21kyz1f3z1",
"hardware_id": "hw_3merk33gt2a7grgmljhh",
"card_id": "crd_3ykdnnld7d57x83eqcm1"
},
"created_at": "2024-03-13T16:56:51.766836837Z"
}
],
"has_next": true,
"cursor_next": "i9vmCnOgONT2AjUMCn1K1N5cJg=="
}
Get event
event ID
Response Example (200 OK)
{
"id": "evt_3merk33gt21kym11een1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"subject": {
"member_id": "mem_3merk33gt7ml3tde71f3",
"member_email_id": "me_3rkd7ya2pnjysjqbluj1",
"member_magic_link_id": "mml_3rkd2f2mv925ptvpgblh",
"member_pin_id": "mp_3rkd2fmgmmdh1dgkvluh",
"member_card_id": "mc_3yd55tbrelfxhjjvkdqh",
"member_sdk_token_id": "mst_3yd55tbrelfxhjjvkdqh"
},
"verb": "use",
"object": {
"type": "gadget_action",
"device_id": "dev_3merk33gt3l525ryhcmh",
"gadget_id": "gad_3merk33gt1hnl6pvbu71",
"gadget_action_id": "open",
"member_id": "mem_3merk33gt7ml3tde71f3",
"member_email_id": "me_3rkd7ya2pnjysjqbluj1",
"member_magic_link_id": "mml_3rkd2f2mv925ptvpgblh",
"member_group_id": "mg_3merk33gt1692dk2p2m1",
"member_group_association_id": "mga_3rkd81x2hc3qluv56pl1",
"member_pin_id": "mp_3rkd2fmgmmdh1dgkvluh",
"member_card_id": "mc_3yd55tbrelfxhjjvkdqh",
"member_sdk_token_id": "mst_3yd55tbrelfxhjjvkdqh",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"site_id": "site_3merk33gt21kym11een1",
"webhook_id": "wh_3merk33gt21kyz1f3z1",
"hardware_id": "hw_3merk33gt2a7grgmljhh",
"card_id": "crd_3ykdnnld7d57x83eqcm1"
},
"created_at": "2024-03-13T16:56:51.766836837Z"
}
Webhooks
Webhooks allow your server to receive real-time notifications about events happening in the Akiles organization.
Webhooks send a POST request to the indicated url when a matching event occurs. The request body is a JSON-encoded event, same as you would get in the response of the "get event" endpoint.
URL can be HTTP or HTTPS. We strongly recommend using HTTPS for production.
Filtering
Each webhook has a "filter" which specifies which events should be sent to it. A filter is a list of "filter rules". Each rule specifies:
object_type
: The value ofobject.type
to match. This must be specified, i.e. it's NOT possible to subscribe to all object types.verb
: The value ofverb
to match. If it's set tonull
it matches all verbs, i.e. it IS possible to subscribe to all events about one object type, all verbs.
The webhook filter matches if any of the rules in the array match.
Some examples:
"filter": []
: Matches no events. Events match if at least one filter rule matches, which can't happen if there's no rules at all."filter": [{"verb": "use", "object_type": "gadget_action"}]
: Matches gadget action events."filter": [{"object_type": "member"}]
: Matches all events related to members (creation, edition, deletion...)"filter": [{"verb": "edit", "object_type": "member"}]
: Matches member edition events."filter": [{"verb": "edit", "object_type": "member"}, {"verb": "use", "object_type": "gadget_action"}]
: Matches member editions, and gadget actions.
An event will be sent to this webhook if it matches at least one rule. If it matches multiple rules, it will only be sent once. If it matches multiple webhook objects, it will be sent once per webhook to its respective URL, even if the URL is the same.
Signature
If an attacker learns the URL of your webhook, they could send requests themselves to it, spoofing webhooks. To prevent this, Akiles signs the webhook requests so that your server can check they come from Akiles and not an attacker. It's strongly recommended to verify the signature.
The signature is the result of applying HMAC-SHA256 to the raw request body bytes, using the webhook secret as a key. It's sent in the X-Akiles-Sig-SHA256
HTTP header.
The secret is returned when you create the webhook. It's not possible to view the secret of an existing webhook, you have to delete and recreate it instead.
The following is an example of how to receive webhooks and validate the signature using nodejs:
import url from 'url'
import http from 'http'
import crypto from 'crypto'
// replace with the secret you get when creating the webhook.
const webhookSecret = 'f305d484fd10de285b00bb203659e863';
const app = http.createServer((request, response) => {
let body = [];
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
const gotSig = Buffer.from(request.headers['x-akiles-sig-sha256'], 'hex');
const expectedSig = crypto.createHmac("sha256", webhookSecret).update(body).digest();
const ok = crypto.timingSafeEqual(gotSig, expectedSig);
if (!ok) {
response.writeHead(400);
response.write('bad sig');
response.end();
return;
}
const event = JSON.parse(body.toString());
console.log('Got event!');
console.log(event);
response.writeHead(200);
response.write('cool');
response.end();
});
});
app.listen(8088);
Delivery
Webhook delivery is considered successful if your server returns a 2xx status code.
Unsuccessful deliveries are retried, with an exponential backoff starting at a few seconds and doubling every time, for up to 1 hour.
webhook_filter_rule
object_type string gadget, gadget_action, member, member_group, organization, site | Object type of the event. |
verb string create, edit, delete, use | Action verb of the event. If null, matches all verbs. |
Example
{
"object_type": "gadget_action",
"verb": "use"
}
webhook
id string (webhook_id) | webhook ID |
organization_id string (organization_id) | organization ID |
filter webhook_filter_rule | Array of event filter rules. An event will be sent to this webhook if it matches at least one rule. If it matches multiple rules, it will only be sent once. If it matches multiple webhook objects, it will be sent once per webhook to its respective URL. |
url string | URL the events are sent to. |
secret string | Secret used to sign webhooks. Visible only in the response for the creation API call. |
is_enabled boolean | True if this webhook is enabled. When false, no webhooks are sent. |
is_deleted boolean | True if this object has been deleted. |
created_at string (date-time) | Creation time for this object. |
Example
{
"id": "wh_3merk33gt21kyz1f3z1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"filter": [
{
"object_type": "gadget_action",
"verb": "use"
}
],
"url": "https://example.com/hook",
"secret": "f305d484fd10de285b00bb203659e863",
"is_enabled": true,
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z"
}
List webhooks
Cursor value obtained from a previous request to the same endpoint. If not present, the first page of results is returned.
Items per page.
Sort order. Field name, followed by :asc
or :desc
. Defaults to id:desc
. In most models, IDs are assigned in increasing order in time, so sorting by id
is equivalent to sorting by created_at
. Depending on the model, sorting on some fields might not be supported.
data webhook | |
has_next boolean | |
cursor_next string (cursor) |
Response Example (200 OK)
{
"data": [
{
"id": "wh_3merk33gt21kyz1f3z1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"filter": [
{
"object_type": "gadget_action",
"verb": "use"
}
],
"url": "https://example.com/hook",
"secret": "f305d484fd10de285b00bb203659e863",
"is_enabled": true,
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z"
}
],
"has_next": true,
"cursor_next": "i9vmCnOgONT2AjUMCn1K1N5cJg=="
}
Create webhook
filter webhook_filter_rule | Array of event filter rules. |
url string | URL the events are sent to. |
is_enabled boolean | True if this webhook is enabled. When false, no webhooks are sent. |
Request Example
{
"filter": [
{
"object_type": "gadget_action"
}
],
"url": "https://example.com/hook",
"is_enabled": true
}
Response Example (200 OK)
{
"id": "wh_3merk33gt21kyz1f3z1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"filter": [
{
"object_type": "gadget_action",
"verb": "use"
}
],
"url": "https://example.com/hook",
"secret": "f305d484fd10de285b00bb203659e863",
"is_enabled": true,
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z"
}
Get webhook
webhook ID
Response Example (200 OK)
{
"id": "wh_3merk33gt21kyz1f3z1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"filter": [
{
"object_type": "gadget_action",
"verb": "use"
}
],
"url": "https://example.com/hook",
"secret": "f305d484fd10de285b00bb203659e863",
"is_enabled": true,
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z"
}
Edit webhook
webhook ID
filter webhook_filter_rule | Array of event filter rules. |
url string | URL the events are sent to. |
is_enabled boolean | True if this webhook is enabled. When false, no webhooks are sent. |
Request Example
{
"filter": [
{
"object_type": "gadget_action"
}
],
"url": "https://example.com/hook",
"is_enabled": true
}
Response Example (200 OK)
{
"id": "wh_3merk33gt21kyz1f3z1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"filter": [
{
"object_type": "gadget_action",
"verb": "use"
}
],
"url": "https://example.com/hook",
"secret": "f305d484fd10de285b00bb203659e863",
"is_enabled": true,
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z"
}
Delete webhook
webhook ID
Response Example (200 OK)
{
"id": "wh_3merk33gt21kyz1f3z1",
"organization_id": "org_3merk33gt1v9ypgfzrp1",
"filter": [
{
"object_type": "gadget_action",
"verb": "use"
}
],
"url": "https://example.com/hook",
"secret": "f305d484fd10de285b00bb203659e863",
"is_enabled": true,
"is_deleted": false,
"created_at": "2024-03-13T16:56:51.766836837Z"
}