Skip to content

Authentication

Every request to the OpenAlarm Core API (api.openalarm.io) is authenticated with an account-level API key.

In the OpenAlarm console, go to Developers → API Keys, click New API Key, and create the key. Keys can be scoped to specific alarms and panic buttons - the right shape for a key that lives in one webhook config.

Every key starts with the oa_ prefix followed by 64 characters - the prefix is what tells a key apart from a resource ID at a glance:

oa_m9s346q3d25vt4f5v37e3s3e28jt97kb6cq643dzvmxxqkfbf5kznwj47tan9zt2

The key authorizes the alarm actions (arm, disarm, trigger, clear, panic) plus a small read-only integration surface - the inventory and live state the official Home Assistant integration is built on, fenced to whatever the key can fire. It cannot change configuration or access your account.

Send the key on every request, one of two equivalent ways. X-API-Key is the canonical form; an Authorization: Bearer token is also accepted.

X-API-Key: oa_m9s346q3d25vt4f5v37e3s3e28jt97kb6cq643dzvmxxqkfbf5kznwj47tan9zt2
Terminal window
curl \
-H "X-API-Key: oa_m9s346q3d25vt4f5v37e3s3e28jt97kb6cq643dzvmxxqkfbf5kznwj47tan9zt2" \
https://api.openalarm.io/v1/alarm/k7m3x9q2f8d4w1b5n6p0r3t7v2x5z9c4/trigger

Send the key in the Authorization header as a bearer token - the word Bearer, a single space, then your key:

Authorization: Bearer oa_m9s346q3d25vt4f5v37e3s3e28jt97kb6cq643dzvmxxqkfbf5kznwj47tan9zt2
Terminal window
curl \
-H "Authorization: Bearer oa_m9s346q3d25vt4f5v37e3s3e28jt97kb6cq643dzvmxxqkfbf5kznwj47tan9zt2" \
https://api.openalarm.io/v1/alarm/k7m3x9q2f8d4w1b5n6p0r3t7v2x5z9c4/trigger

Every endpoint is a plain GET and requests are bodyless - the URL is the whole call. The key gates the request; the ID in the path names which alarm or panic button it acts on. (POST is reserved for future endpoints that accept a request body.)

Send the key as a header only. OpenAlarm never accepts an API key in the URL as a query parameter, and the alarm ID is not a secret - it identifies an alarm, it does not authorize anything. That is what makes a GET endpoint safe: a URL on its own cannot fire an alarm.

Status Meaning
202 Accepted. Every successful call returns this.
401 Unauthorized - your API key is missing or invalid.
404 Alarm ID not found / Panic ID not found.

There is one success code. Arming, disarming, and triggering all return 202 Accepted - the call has been accepted and will be applied. For a trigger, alerts go out after the false alarm delay, so nothing has reached your contacts at the moment you get the response. You never have to branch on which 2xx came back.

202 does not promise anyone was alerted. A trigger that folds into an open incident, lands in test mode, or is cancelled inside the false alarm delay is accepted and recorded without reaching anyone - see Arming, which walks the list if your webhook fires and nothing happens.

The two failures never overlap. 401 always means the problem is your API key. 404 always means your key is fine and the trigger named in the path is not one you can fire right now - whether it does not exist, is not yours, is outside your key’s scope, or is disabled or deactivated by your plan. Those are deliberately indistinguishable, so a narrowly-scoped key cannot be used to work out which IDs are real. Nothing else is returned for authentication or lookup.

Repeat triggers are accepted, not rejected. You do not need to handle a separate case for an alarm that is already triggered - if an incident is already open the trigger is recorded against it and you still get 202. Several sensors triggering at once therefore never alert your contacts twice.

Every response the API produces uses the same four-field shape:

{
"error": false,
"message": null,
"traceId": "8f14e45f-ea0d-4a1b-9f2c-6b3d70c5e881",
"data": {
"environment": "live",
"location": { "id": "2c9wvhrq52k5refhtvmmr0f8yjz5j9e9", "name": "Home" }
}
}

error is the outcome. message is human-readable and null on success. data carries the payload: the location the source belongs to, and environment, which tells you whether the call ran against your real contacts (live) or as a test (test). Check it if your automation depends on being live; an alarm left in test mode looks identical from the outside otherwise.

The one exception is 401, which is generated before your request reaches the API and returns {"message":"Unauthorized"} on its own.

traceId identifies that one call. It is not an alarm, account, or incident reference and grants no access to anything.

Quote it when you report a problem. It resolves exactly what happened to a single request, which is otherwise hard to pin down after the fact - so logging it alongside your own automation’s records is worth doing.

See the API Reference for the full endpoint contract.