Deleting personal access tokens via the API


I’m having a problem with deleting personal access tokens via the API. Any tokens I ‘delete’ this way only gets flagged as ‘revoked’, but they are not actually removed from the list. If I click on the revoke button manually in the graphical UI, the token actually gets deleted, but never from the API.

This is a problem for us since we would like to generate a new personal access token each time a user logs in (to another site which interacts with Gitlab), but if we can’t delete old tokens they quickly start to pile up… I would really rather not have to store personal access tokens in a separate db, so I’m hoping that I’m just doing something wrong although I can’t see what.


can you please share the API calls you are using? I haven’t had the chance yet to try this endpoint.


Hi Michael,

Sure, it’s just (for token with ID 67):

curl -k -X DELETE -w “%{http_code}” -H “PRIVATE-TOKEN:-REDACTED-”

And what I get as output to that is just “204”, which seems to indicate that everything is fine and the token does get revoked, but just never deleted.

Thanks for trying to help!


thanks, this helps to try it without further reading :slight_smile:

This command lists all the personal access tokens. jq is used for better formatting JSON on the shell, GITLAB_TOKEN is exported into my environment in a .env file for ZSH.


Checking the GUI for the ID I can delete. This one is from an older webcast which is not active anymore. Good way to clean it away.

Then I had an idea on listing and filtering this with jq. This resulted in writing a full blog post about jq and listing&revoking GitLab PATs:

TL;DR - after inspecting the API docs, and comparing the request method with DELETE, it became clear to me that PATs can only be revoked but not deleted. This is common practice to keep certificates and tokens, but revoke them. Later audit logs and processes can be dependent on this, matching the PAT with the user id and so on.

The UI uses a filter to not list revoked PATs. Your curl API call needs to filter the received result by checking on revoked == false. jq needs to be installed for the following command to work:

$ curl -H "PRIVATE-TOKEN: $GITLAB_TOKEN" 2&>1 >/dev/null | jq -c '.[] | select( .revoked == false)' | jq

Kind regards,



I appreciate you taking the time to look into this Michael, and I understand the reasoning behind never deleting access tokens. However this causes some problems in our use case, and I suspect we may not be alone in that. There is also another problem which helps compound the first one, which is that when you fetch the list of tokens via the API, you will only get a maximum of 20 returned, and there doesn’t seem to be any provisioning for paging, meaning if you have over 20 tokens, there’s no way to be sure you can revoke them all via the API.

To further add to the confusion, pressing the “Revoke” button in the UI actually does seem to delete - not just revoke - the token. So we have a situation where the “Delete” function via the API actually performs a revoke, and the “Revoke” function in the UI actually performs a delete. I understand that it’s not always easy to maintain consistency in these sort of things when building systems, but I would just like to point out that this is not ideal from a user experience perspective and it would probably be good if it was changed somehow.

To go back to the more central issue, I understand that it might be desirable to never allow deletion of tokens, and I think our use case would be resolved if it was possible to query the token list with a parameter filtering out all of the revoked tokens. I understand that it’s possible to do this client side, but that results in a lot of data being shuffled needlessly which slows down operations.

Something would probably also have to be done for the list of tokens in the UI, to keep it manageable when it contains hundreds or thousands of tokens.


appreciate the thoughtful feedback. I think it is worth a discussion with our engineering teams, and see how we can find a solution for your use case. Mind opening an issue? (Bug template could also fit, unsure here).