# Web API

A Homey app can add its own endpoints to Homey's Web API (REST + Realtime), to allow for external access. As an example, you could use this API to enable a Raspberry PI to reports its status to Homey.

{% hint style="info" %}
Apps on Homey Cloud are not allowed to expose a Web API. Read more about this in the [Homey Cloud guide](https://apps.developer.homey.app/guides/homey-cloud).
{% endhint %}

Your app's API endpoints are available under the following url: `/api/app/com.yourapp.id/`. All endpoints are protected by default, and the requesting user needs permission to your app (which is granted by default after installation). You can override this by setting `"public": true`.

To add API endpoints to an app, start by defining the routes in the [App Manifest](https://apps.developer.homey.app/the-basics/app/manifest). The key of each route corresponds to the name of a function you define. The following route options can be configured in the App Manifest:

| key      | type              | value                                                                             |
| -------- | ----------------- | --------------------------------------------------------------------------------- |
| `method` | `String`, `Array` | `"GET"`, `"POST"`, `"PUT"` or `"DELETE"`, or an array of these values.            |
| `path`   | `String`          | for example `"/"`, `"/:foo"`, `"/bar/:foo"`                                       |
| `public` | `Boolean`         | Default: `false`, set to `true` to make this endpoint accessible without a token. |

{% hint style="warning" %}
Only use public endpoints when no alternatives are possible. A good usecase for a public endpoint is sending a pin-code from another device to Homey.
{% endhint %}

{% tabs %}
{% tab title="JavaScript" %}
In the following example we define four routes named `getSomething`, `addSomething`, `updateSomething` and `deleteSomething` in `api.js`:

{% code title="/.homeycompose/app.json" %}

```javascript
  "api": {
    "getSomething": {
      "method": "GET",
      "path": "/"
    },
    "addSomething": {
      "method": "POST",
      "path": "/"
    },
    "updateSomething": {
      "method": "PUT",
      "path": "/:id"
    },
    "deleteSomething": {
      "method": "DELETE",
      "path": "/:id"
    }
  }
```

{% endcode %}

The implementation of each route is defined in the `api.js` file, this file should export async functions with names that correspond to the names defined in the App Manifest. For example:

{% code title="/api.js" %}

```javascript
module.exports = {
  async getSomething({ homey, query }) {
    // you can access query parameters like "/?foo=bar" through `query.foo`

    // you can access the App instance through homey.app
    const result = await homey.app.getSomething();

    // perform other logic like mapping result data

    return result;
  },

  async addSomething({ homey, body }) {
    // access the post body and perform some action on it.
    return homey.app.addSomething(body);
  },

  async updateSomething({ homey, params, body }) {
    return homey.app.updateSomething(params.id, body);
  },

  async deleteSomething({ homey, params }) {
    return homey.app.deleteSomething(params.id);
  },
};
```

{% endcode %}
{% endtab %}

{% tab title="TypeScript" %}
In the following example we define four routes named `getSomething`, `addSomething`, `updateSomething` and `deleteSomething` in `api.mts`:

{% code title="/.homeycompose/app.json" %}

```javascript
  "api": {
    "getSomething": {
      "method": "GET",
      "path": "/"
    },
    "addSomething": {
      "method": "POST",
      "path": "/"
    },
    "updateSomething": {
      "method": "PUT",
      "path": "/:id"
    },
    "deleteSomething": {
      "method": "DELETE",
      "path": "/:id"
    }
  }
```

{% endcode %}

The implementation of each route is defined in the `api.mts` file, this file should export async functions with names that correspond to the names defined in the App Manifest. For example:

{% code title="/api.mts" %}

```mts
import type App from "./app.mjs";

type RequestWithBody = {
  homey: App["homey"];
  query: Record<string, string>;
  params: Record<string, string>;
  body: Record<string, unknown>;
};

type RequestWithoutBody = {
  homey: App["homey"];
  query: Record<string, string>;
  params: Record<string, string>;
  body: Record<never, never>; // Homey.API sends an empty body for GET and DELETE requests
};

export default {
  async getSomething({ homey, query }: RequestWithoutBody): Promise<any> {
    // you can access query parameters like "/?foo=bar" through `query.foo`

    // you can access the App instance through homey.app
    const result = await (homey.app as App).getSomething();

    // perform other logic like mapping result data

    return result;
  },

  async addSomething({ homey, body }: RequestWithBody): Promise<any> {
    // access the post body and perform some action on it.
    return (homey.app as App).addSomething(body);
  },

  async updateSomething({ homey, params, body }: RequestWithBody): Promise<any> {
    return (homey.app as App).updateSomething(params.id, body);
  },

  async deleteSomething({ homey, params }: RequestWithoutBody): Promise<any> {
    return (homey.app as App).deleteSomething(params.id);
  },
};

```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
In the following example we define four routes named `get_something`, `add_something`, `update_something` and `delete_something` in `api.py`:

{% code title="/.homeycompose/app.json" %}

```javascript
  "api": {
    "get_something": {
      "method": "GET",
      "path": "/"
    },
    "add_something": {
      "method": "POST",
      "path": "/"
    },
    "update_something": {
      "method": "PUT",
      "path": "/:id"
    },
    "delete_something": {
      "method": "DELETE",
      "path": "/:id"
    }
  }
```

{% endcode %}

The implementation of each route is defined in the `api.py` file, this file should export async functions with names that correspond to the names defined in the App Manifest. For example:

{% code title="/api.py" %}

```python
from typing import Any, Never, cast

from homey.homey import Homey

from .app import App


async def get_something(
    *,
    homey: Homey,
    query: dict[str, str],
    params: dict[str, str],
    body: dict[Never, Never],  # Homey.API sends an empty body for GET requests
) -> Any:
    # you can access query parameters like "/?foo=bar" through `query.get("foo")`

    # you can access the App instance through homey.app
    result = cast(App, homey.app).get_something()

    # perform other logic like mapping result data

    return result


async def add_something(
    *, homey: Homey, query: dict[str, str], params: dict[str, str], body: dict[str, Any]
) -> Any:
    return cast(App, homey.app).add_something(body)


async def update_something(
    *, homey: Homey, query: dict[str, str], params: dict[str, str], body: dict[str, Any]
) -> Any:
    return cast(App, homey.app).update_something(body)


async def delete_something(
    *,
    homey: Homey,
    query: dict[str, str],
    params: dict[str, str],
    body: dict[Never, Never],  # Homey.API sends an empty body for DELETE requests
) -> Any:
    return cast(App, homey.app).delete_something(params["id"])


# Export all these methods as endpoints
__all__ = ["get_something", "add_something", "update_something", "delete_something"]

```

{% endcode %}
{% endtab %}
{% endtabs %}

Api functions receive an object as their argument, this object has four properties: `homey`, `params`, `query` and `body`.

* `homey` is the Homey instance. Using this instance you can, for example, access the App instance.
* `body` is an object with the request body, when your request has method `POST` or `PUT`. JSON is automatically parsed.
* `params` is a set of strings defined in your `path`.
* `query` is a set of strings that are provided as query parameters, for example `?foo=bar` will result in `{ "foo": "bar" }`.

## Realtime events

Your app can emit 'realtime' events, which are one-way events to a subscribing client, for example a browser showing a settings view page.

{% tabs %}
{% tab title="JavaScript" %}
{% code title="/app.js" %}

```javascript
const Homey = require('homey');

class App extends Homey.App {
  async onInit() {
    await this.homey.api.realtime("my_event", "my_json_stringifyable_value");
  }
}

module.exports = App;
```

{% endcode %}
{% endtab %}

{% tab title="TypeScript" %}
{% code title="/app.mts" %}

```mts
import Homey from "homey";

export default class App extends Homey.App {
  async onInit(): Promise<void> {
    await this.homey.api.realtime("my_event", "my_json_stringifyable_value");
  }
}

```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code title="/app.py" %}

```python
from homey import app


class App(app.App):
    async def on_init(self) -> None:
        await self.homey.api.realtime("my_event", "my_json_stringifyable_value")


homey_export = App

```

{% endcode %}
{% endtab %}
{% endtabs %}

## Accessing the Web API of another app

Apps can also talk to each other through their API's, however you need to define the correct permissions first. Permissions for app to app communication look like this `homey:app:<appId>`, for example `homey:app:com.athom.example` or `homey:app:com.yahoo.weather`.

In order to communicate with another app you first need to create a `ApiApp` client.

The Homey Apps SDK provides some information about the app you are connecting to. For example whether it is installed on the Homey your app is installed on and what version of the app is installed. You can even subscribe to events that get emitted when an app is installed or uninstalled.

{% tabs %}
{% tab title="JavaScript" %}
{% code title="/app.js" %}

```javascript
const Homey = require('homey');

class App extends Homey.App {
  async onInit() {
    this.otherAppApi = this.homey.api.getApiApp('com.athom.otherApp');

    const isInstalled = await this.otherAppApi.getInstalled();
    const version = await this.otherAppApi.getVersion();

    this.otherAppApi.on('install', () => {
      console.log('otherApp is installed');
    });

    this.otherAppApi.on('uninstall', () => {
      console.log('otherApp is uninstalled');
    });
  }
}

module.exports = App;
```

{% endcode %}
{% endtab %}

{% tab title="TypeScript" %}
{% code title="/app.mts" %}

```mts
import Homey, { ApiApp } from "homey";

export default class App extends Homey.App {
  otherAppApi!: ApiApp;

  async onInit(): Promise<void> {
    this.otherAppApi = this.homey.api.getApiApp("com.athom.otherApp");

    const isInstalled = await this.otherAppApi.getInstalled();
    const version = await this.otherAppApi.getVersion();

    this.otherAppApi.on("install", () => {
      console.log("otherApp is installed");
    });

    this.otherAppApi.on("uninstall", () => {
      console.log("otherApp is uninstalled");
    });
  }
}

```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code title="/app.py" %}

```python
from homey import app
from homey.api_app import ApiApp


class App(app.App):
    other_app_api: ApiApp

    async def on_init(self) -> None:
        self.other_app_api = self.homey.api.get_api_app("com.athom.otherApp")

        is_installed = await self.other_app_api.get_installed()
        version = await self.other_app_api.get_version()

        def on_install():
            print("otherApp is installed")

        self.other_app_api.on_install(on_install)

        def on_uninstall():
            print("otherApp is uninstalled")

        self.other_app_api.on_uninstall(on_uninstall)


homey_export = App

```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
Always check whether the target app is installed and has a compatible version before trying to send requests. Failing to do so may cause your app to break unexpectedly when the target app is updated.
{% endhint %}

You can interact with the Web API of the target app by making requests and listening to realtime events. Which APIs and events you can expect is up to the target app, so make sure to check the source code, read the documentation or ask the app's developer.

{% tabs %}
{% tab title="JavaScript" %}
{% code title="/app.js" %}

```javascript
const Homey = require('homey');

class App extends Homey.App {
  async onInit() {
    this.otherAppApi = this.homey.api.getApiApp("com.athom.otherApp");

    // Make a get request to "otherApp"s API
    const getResponse = await this.otherAppApi.get('/');

    // Post some data to "otherApp", the second argument is the request body
    const postResponse = await this.otherAppApi.post('/play', { sound: 'bell' });

    // Listen to app realtime events
    this.otherAppApi.on('realtime', (event) => {
      console.log('otherApp.onRealtime', event);
    });
  }
}

module.exports = App;
```

{% endcode %}
{% endtab %}

{% tab title="TypeScript" %}
{% code title="/app.mts" %}

```mts
import Homey, { ApiApp } from "homey";

export default class App extends Homey.App {
  otherAppApi!: ApiApp;

  async onInit(): Promise<void> {
    this.otherAppApi = this.homey.api.getApiApp("com.athom.otherApp");

    // Make a get request to "otherApp"s API
    const getResponse = await this.otherAppApi.get("/");

    // Post some data to "otherApp", the second argument is the request body
    const postResponse = await this.otherAppApi.post("/play", { sound: "bell" });

    // Listen to app realtime events
    this.otherAppApi.on("realtime", event => {
      console.log("otherApp.onRealtime", event);
    });
  }
}

```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code title="/app.py" %}

```python
from homey import app
from homey.api_app import ApiApp


class App(app.App):
    other_app_api: ApiApp

    async def on_init(self) -> None:
        self.other_app_api = self.homey.api.get_api_app("com.athom.otherApp")

        # Make a get request to "otherApp"s API
        get_response = await self.other_app_api.get("/")

        # Post some data to "otherApp", the second argument is the request body
        post_response = await self.other_app_api.post("/play", {"sound": "bell"})

        # Listen to app realtime events
        def on_realtime(event, *args):
            print("otherApp.onRealtime", event)

        self.other_app_api.on_realtime(on_realtime)


homey_export = App

```

{% endcode %}
{% endtab %}
{% endtabs %}
