diff --git a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.tokens.tsx b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.tokens.tsx
index 239137d0b..fa38b9766 100644
--- a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.tokens.tsx
+++ b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.tokens.tsx
@@ -68,6 +68,20 @@ export default function ApiTokensPage() {
return i18n.date(row.original.expires);
},
},
+ {
+ header: t`Last Used`,
+ cell: ({ row }) => {
+ if (!row.original.lastUsedAt) {
+ return (
+
+ Never
+
+ );
+ }
+
+ return {i18n.date(row.original.lastUsedAt)};
+ },
+ },
{
header: t`Actions`,
cell: ({ row }) => (
diff --git a/packages/lib/server-only/public-api/get-api-token-by-token.ts b/packages/lib/server-only/public-api/get-api-token-by-token.ts
index d25b9b902..96309161d 100644
--- a/packages/lib/server-only/public-api/get-api-token-by-token.ts
+++ b/packages/lib/server-only/public-api/get-api-token-by-token.ts
@@ -96,6 +96,17 @@ export const getApiTokenByToken = async ({ token, bypassRateLimit = false }: Get
});
}
+ void prisma.apiToken
+ .update({
+ where: {
+ id: apiToken.id,
+ },
+ data: {
+ lastUsedAt: new Date(),
+ },
+ })
+ .catch(() => null);
+
return {
...apiToken,
user,
diff --git a/packages/lib/server-only/public-api/get-api-tokens.ts b/packages/lib/server-only/public-api/get-api-tokens.ts
index 06e014a10..a97ba809d 100644
--- a/packages/lib/server-only/public-api/get-api-tokens.ts
+++ b/packages/lib/server-only/public-api/get-api-tokens.ts
@@ -22,6 +22,7 @@ export const getApiTokens = async ({ userId, teamId }: GetApiTokensOptions) => {
name: true,
createdAt: true,
expires: true,
+ lastUsedAt: true,
},
orderBy: {
createdAt: 'desc',
diff --git a/packages/prisma/migrations/20260818081941_add_api_token_last_used_property/migration.sql b/packages/prisma/migrations/20260818081941_add_api_token_last_used_property/migration.sql
new file mode 100644
index 000000000..9b9f88927
--- /dev/null
+++ b/packages/prisma/migrations/20260818081941_add_api_token_last_used_property/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "ApiToken" ADD COLUMN "lastUsedAt" TIMESTAMP(3);
diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma
index 06be2eed9..13768e34f 100644
--- a/packages/prisma/schema.prisma
+++ b/packages/prisma/schema.prisma
@@ -220,16 +220,17 @@ enum ApiTokenAlgorithm {
}
model ApiToken {
- id Int @id @default(autoincrement())
- name String
- token String @unique
- algorithm ApiTokenAlgorithm @default(SHA512)
- expires DateTime?
- createdAt DateTime @default(now())
- userId Int?
- user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
- teamId Int
- team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
+ id Int @id @default(autoincrement())
+ name String
+ token String @unique
+ algorithm ApiTokenAlgorithm @default(SHA512)
+ expires DateTime?
+ createdAt DateTime @default(now())
+ lastUsedAt DateTime?
+ userId Int?
+ user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
+ teamId Int
+ team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
}
enum SubscriptionStatus {
diff --git a/packages/trpc/server/api-token-router/get-api-tokens.types.ts b/packages/trpc/server/api-token-router/get-api-tokens.types.ts
index 6a690066d..c3f06d916 100644
--- a/packages/trpc/server/api-token-router/get-api-tokens.types.ts
+++ b/packages/trpc/server/api-token-router/get-api-tokens.types.ts
@@ -9,6 +9,7 @@ export const ZGetApiTokensResponseSchema = z.array(
name: true,
createdAt: true,
expires: true,
+ lastUsedAt: true,
}),
);