A machine is a caller that is not a person: a nightly job, a backend, a CI pipeline, or an AI agent. It has no password, no email and no sign-in. It has a name, a kind, roles and permissions of its own, and one or more keys. Use a machine instead of a user API key when the caller should not borrow a person’s identity:

Two kinds

The kind changes nothing about what a machine may do. It is recorded in the token and in $auth.actor, so a policy or a report can tell an agent from a script.

Turn it on

Machines are off until you enable them in the directory settings.

In Studio

Users & auth → Developers → Machines & agents.
  • Configure holds the settings in the table above. Turn machines on here first.
  • New machine asks what it is (a service or an AI agent), its name, its roles and any extra permissions.
  • Open a machine to see its client ID, change its roles and permissions, disable or delete it, and manage its keys.
  • New key takes an optional name, an optional list of permissions to limit it to, and how many days it lasts. The key is shown once, next to the client ID, with a copy button.

Create a machine from code

Machines can also be administered with the environment’s secret key, from your server or a script.

What a machine can do

A machine’s permissions are its own permissions plus those of each role. A key’s scopes can only narrow that set. orders:* covers orders:read and orders:write. A key with no scopes carries all of the machine’s permissions.

Sign in

There are two ways, and both end in the same kind of token.

Exchange the key for a token

This is the OAuth client_credentials grant. Use it for agents and long-running services: the token is short-lived and carries only what you asked for.
The answer:
scope is the permissions the token carries. Asking for a scope can only reduce them; a permission the key does not allow is dropped, not an error. In TypeScript, asMachine signs in the first time it needs a token, reuses it until 30 seconds before it expires, and signs in again on its own. In Python, as_machine returns a view with one token; take a new view when expires_in runs out. Without an SDK, send the token as Authorization: Bearer <access_token> with the usual apikey header.

Send the key itself

The simplest option for a script. Send the key as the bearer credential, next to the project key:
The edge swaps the key for a 5-minute access token before the request reaches your rules, exactly as it does for a person’s pbu_ key. A revoked key is refused within about 45 seconds.

What the token says

A machine’s token is an ordinary access token with these differences:

Rules and policies

Every rule that works for a signed-in user works for a machine, using the machine’s permissions. To tell the two apart, use Compare values in the rule picker with $auth.actor_type:
  • Only machines: $auth.actor_type equals machine
  • Only agents: $auth.actor.kind equals agent
  • Never a machine: $auth.actor_type equals user
Combine it with Has permission as usual. For example, let only the support agent read orders: All of Has permission orders:read, and $auth.actor.name equals support-agent. See Policy context for the full $auth object.
A machine is kind: "user" in $auth.kind so existing policies keep working. If a rule must be for people only (changing a password, deleting an account, approving a payment), require $auth.actor_type equals user.
Account routes that only make sense for a person (/auth/v1/user, sessions, MFA, devices, a person’s own API keys) answer 403 for a machine.

Manage machines

Listings never include a secret. Each key shows its prefix, scopes, status, expires_at, last_used_at, last_ip and use_count. To rotate a key, create the new key, deploy it, then revoke the old one. A machine can hold several keys for exactly this reason. Disabling a machine refuses its keys at once. A token it already holds works until it expires (at most the token lifetime, 15 minutes by default, or 5 minutes for a bearer key). The events machine.created, machine.updated, machine.deleted, machine.key_created and machine.key_revoked are available to webhooks.

Errors

Good practice

  • Give each agent its own machine. One shared identity makes every log line ambiguous.
  • Start from no permissions and add only what the agent needs. An agent that answers order questions does not need orders:write.
  • Put a scope on every key, give it a short life (30 to 90 days), and rotate it.
  • Keep the key in a secret store or environment variable. Never put it in a prompt, a tool description, or a repository.
  • For actions with real consequences, require a person: add a rule $auth.actor_type equals user, or have the agent create a request that a person approves.

What this does not do yet

  • A machine cannot act on behalf of a person. If an agent needs the person’s own access, use that person’s user API key.
  • There is no token exchange from an outside identity (for example a CI provider’s OIDC token). The key is the credential.

User API keys

Keys that act as a person.

Policy context

Everything a rule can read about the caller.