Register your interest: Tag @Cody, get an agent
BlogEngineering

Home Assistant tokens: ten years of full access, and no scopes

Creating a long-lived access token, what it can actually do, why there is no way to limit it, and the exposure decisions that follow.

Amman VediAmman Vedi11 min read

Summarize with AI

Home Assistant tokens: ten years of full access, and no scopes
On this page

Creating a Home Assistant token takes about fifteen seconds, which is roughly the amount of attention most people give the thing they have just created. It is worth more than that, because a long-lived access token is valid for ten years, cannot be limited to particular devices, and carries the rights of the account that made it.

That combination means the token is not really a credential for an integration. It is a key to the house, with a decade on the clock, and the sensible handling follows from treating it that way rather than from anything specific to the API.

What we'll cover

What the token actually is

Home Assistant issues short-lived tokens for browser sessions and long-lived tokens for everything else. The long-lived ones are what external scripts, integrations and automation platforms use, and there is only one kind, which is why the two names people search for describe the same object.

Three properties matter.

It lasts ten years. Not a session, not ninety days. Unless you revoke it, it works until well into the next decade.

It cannot be scoped. There is no mechanism to restrict a token to one device, one domain or read-only access. Granular permissions have been an open request for years and are not available.

It inherits the creating account's rights. A token created by an administrator can do everything an administrator can do.

Put together: anyone holding the token can unlock whatever your installation can unlock, for ten years, and nothing in the token system limits that.

Creating one

  1. Open your Home Assistant instance, typically at http://homeassistant.local:8123.
  2. Click your profile avatar at the bottom of the sidebar.
  3. Scroll to the bottom of the profile page, to Long-Lived Access Tokens.
  4. Click Create Token.
  5. Give it a name describing what will use it, such as codewords-heating.
  6. Copy it immediately. It is displayed once and cannot be retrieved afterwards.

The name is the only handle you will ever have on it. A token called test created two years ago, still active, belonging to nobody knows what, is the situation to avoid, and it is entirely created by that one field.

Using it against the API

The token is a bearer token, sent in an Authorization header, against either of two interfaces.

The REST API suits request-and-response work: read a state, call a service, fetch history. Send Authorization: Bearer <token> and Content-Type: application/json, then POST to /api/services/<domain>/<service> to act, or GET /api/states/<entity_id> to read.

The WebSocket API suits anything needing to react. You authenticate once at the start of the connection and then receive events as they happen, which is the right choice when you want to know that a door opened rather than polling to ask.

A rule worth adopting early: read state before acting on it. Sending a turn-on command to something already on is harmless; assuming a state and acting on the assumption is how automations produce confusing results at three in the morning.

The thing with no solution: there are no scopes

Worth sitting with, because most credential advice assumes you can narrow permissions and here you cannot.

There is no way to say that this token may only read the temperature, or may only control the lights in one room, or may not unlock anything. A token that can call one service can call all of them. An integration that only needs to read a sensor gets, unavoidably, the ability to open a garage door.

That has one honest consequence: every integration you give a token to is trusted completely. The decision is not "what should this have access to" but "do I trust this thing with my whole installation", and it should be made deliberately for each one.

For a self-hosted platform this is a reasonable trade, and it is not a defect you can configure away. It is a fact to plan around.

Building it with CodeWords

CodeWords connects to more than 3,000 integrations, and the connection is made once and reused.

  1. Open CodeWords and start a new automation.
  2. Describe what should happen in plain language to Cody, the automation builder: which entities are involved, what triggers the automation, and what it should never do.
  3. Authorize the connection, storing the token as an encrypted secret referenced by name rather than written into the automation.
  4. Describe the exceptions, particularly anything you want to require a confirmation rather than happening automatically.
  5. Use a dedicated token named for this purpose, so revoking it later affects nothing else.

You describe the outcome; Cody builds it, connects it, and deploys it. The free plan covers light use, with Pro at $39 per month and Business at $100 per month as usage grows; details are on the pricing page.

Why the non-admin workaround disappoints

The obvious mitigation is to create a non-administrator user and issue tokens from that account, limiting what the token can reach.

It works less well than it should. Home Assistant's own permission model for non-admin users is limited, and tokens issued by non-admin accounts are reported as not working with a good many integrations, which sends people back to an admin token to get anything running. The community has been asking for token-level permissions for years and the feature request is still open.

So it is worth trying for anything that only reads data, and worth abandoning quickly if the integration misbehaves rather than spending an evening on it. The mitigations that actually work are about exposure and revocation rather than permissions.

Exposure, which is the real decision

Given a token that cannot be limited, the question that matters is who can reach the instance at all.

Do not forward port 8123 to the internet. This is the single most common mistake, and it puts a control plane for your home on the public internet behind one credential.

Prefer a VPN or a tunnel for remote access, so the instance is not publicly reachable.

If it must be public, put TLS in front of it, use a reverse proxy, and configure Home Assistant's trusted proxy settings correctly so it sees real client addresses.

Keep external automation platforms on the smallest surface you can, and prefer outbound webhooks from Home Assistant over inbound API access when the design allows it. A push-out design needs no inbound path at all.

None of this is exotic, and all of it matters more here than the usual credential hygiene, because the credential itself cannot be constrained.

One token per integration

Since tokens cannot be scoped, the only operational control left is separation, and it costs nothing.

Create a distinct token for each thing that connects, named for that thing. Then you can revoke one without breaking the others, you can tell from the profile page what is connected, and you can rotate on a schedule without an outage across everything at once.

The opposite pattern, a single token pasted into five places over two years, means revoking it takes down things you have forgotten about, which is exactly why nobody ever revokes it.

Review the list periodically and delete anything you cannot account for. A token whose purpose nobody remembers is a token to revoke, and if something breaks you will find out immediately what it was.

When it stops working

401 Unauthorized means the token is wrong, revoked, or the header is malformed. Check for the word Bearer and a space before the token, and for a newline picked up when pasting.

403 Forbidden usually means the account behind the token lacks the rights for that action, which is the non-admin case above.

Connection refused or a timeout is a network or URL problem rather than a credential one. Check whether homeassistant.local resolves from wherever the code runs, since that name frequently does not resolve outside the local network.

Certificate errors on a self-signed setup are worth fixing properly rather than disabling verification, since disabling it removes the protection that made exposure acceptable.

It worked and then stopped most often means somebody revoked the token, deleted the user account it belonged to, or restored a backup from before it existed.

Making it survive

One token per integration, named for its purpose.

Store it as a secret, referenced by name, never written into automation code or committed.

Review the token list quarterly and revoke anything unaccounted for.

Keep the instance off the public internet unless you have deliberately decided otherwise.

Write down which token is where, since the profile page shows names and not destinations.

Limits worth knowing about

Tokens cannot be scoped, so every integration is trusted with the whole installation.

Ten years is effectively forever, and nothing prompts you to rotate.

The token is shown once, so losing it means creating another.

Non-admin tokens break many integrations, which limits the obvious mitigation.

Revoking is the only real control, which is the argument for having several tokens rather than one.

What to decide first

Whether the instance is reachable from outside your network, and how. That decision matters more than anything about the token, because a token that cannot be scoped or expired in under a decade is only as safe as the network path to the thing it opens.

Two habits make the difference. Create a separate token for every integration and name it after what uses it, since the alternative is one token everywhere that nobody dares revoke. And prefer having Home Assistant push events out to your automation platform rather than granting inbound access, because a design with no inbound path has no credential to steal.

Frequently asked questions

How do I create a Home Assistant long-lived access token?

Open your profile from the sidebar, scroll to Long-Lived Access Tokens at the bottom, click Create Token, name it after whatever will use it, and copy it immediately. It is shown only once.

Is an access token the same as a long-lived access token?

Yes, for this purpose. Home Assistant issues short-lived tokens for browser sessions; the only kind you create for external use is a long-lived one, which is why both names describe the same object.

How long do they last?

Ten years, unless revoked. Nothing expires them sooner and nothing prompts you to rotate.

Can I limit what a token can do?

No. There is no per-entity or read-only scoping, and granular token permissions remain an open feature request. A token that can call one service can call all of them.

Should I use a non-admin account instead?

It is worth trying for read-only uses, but Home Assistant's non-admin permission model is limited and many integrations do not work with non-admin tokens. Abandon it quickly rather than spending an evening on it.

What is the most important security step?

Controlling who can reach the instance. Do not forward port 8123 to the internet; use a VPN or tunnel, or a properly configured reverse proxy with TLS if it must be reachable.

Why should each integration have its own token?

Because revocation is the only real control available. Separate tokens let you revoke one without breaking the rest, and make the profile page a usable inventory of what is connected.

What does a 401 mean?

A wrong or revoked token, or a malformed header. Check for Bearer followed by a space, and for a stray newline picked up when the token was pasted.

Why can my script not reach homeassistant.local?

That name usually resolves only on the local network. Use the instance's address or hostname as seen from wherever the code actually runs.

My automation worked and then stopped. What happened?

Most often the token was revoked, the user account that created it was deleted, or a backup was restored from before the token existed.

Get started today

Your first workflow is free to build.

Describe what you need. Cody handles the build, the connections, and the deployment.