diff --git a/backend/src/data/ApiAuditLog.ts b/backend/src/data/ApiAuditLog.ts
new file mode 100644
index 00000000..199747ef
--- /dev/null
+++ b/backend/src/data/ApiAuditLog.ts
@@ -0,0 +1,28 @@
+import { BaseRepository } from "./BaseRepository";
+import { getRepository, Repository } from "typeorm/index";
+import { ApiAuditLogEntry } from "./entities/ApiAuditLogEntry";
+import { ApiLogin } from "./entities/ApiLogin";
+import { AuditLogEventData, AuditLogEventType } from "./apiAuditLogTypes";
+
+export class ApiAuditLog extends BaseRepository {
+  private auditLog: Repository<ApiAuditLogEntry<any>>;
+
+  constructor() {
+    super();
+    this.auditLog = getRepository(ApiAuditLogEntry);
+  }
+
+  addEntry<TEventType extends AuditLogEventType>(
+    guildId: string,
+    authorId: string,
+    eventType: TEventType,
+    eventData: AuditLogEventData[TEventType],
+  ) {
+    this.auditLog.insert({
+      guild_id: guildId,
+      author_id: authorId,
+      event_type: eventType as any,
+      event_data: eventData as any,
+    });
+  }
+}
diff --git a/backend/src/data/apiAuditLogTypes.ts b/backend/src/data/apiAuditLogTypes.ts
new file mode 100644
index 00000000..c50cc06d
--- /dev/null
+++ b/backend/src/data/apiAuditLogTypes.ts
@@ -0,0 +1,35 @@
+export const AuditLogEventTypes = {
+  ADD_API_PERMISSION: "ADD_API_PERMISSION",
+  REMOVE_API_PERMISSION: "REMOVE_API_PERMISSION",
+  EDIT_CONFIG: "EDIT_CONFIG",
+};
+
+export type AuditLogEventType = keyof typeof AuditLogEventTypes;
+
+export type AddApiPermissionEventData = {
+  target_id: string;
+  permissions: string[];
+  expires_at: string | null;
+};
+
+export type RemoveApiPermissionEventData = {
+  target_id: string;
+};
+
+export type EditConfigEventData = {};
+
+export interface AuditLogEventData extends Record<AuditLogEventType, unknown> {
+  ADD_API_PERMISSION: {
+    target_id: string;
+    permissions: string[];
+    expires_at: string | null;
+  };
+
+  REMOVE_API_PERMISSION: {
+    target_id: string;
+  };
+
+  EDIT_CONFIG: {};
+}
+
+export type AnyAuditLogEventData = AuditLogEventData[AuditLogEventType];
diff --git a/backend/src/data/entities/ApiAuditLogEntry.ts b/backend/src/data/entities/ApiAuditLogEntry.ts
new file mode 100644
index 00000000..9c613353
--- /dev/null
+++ b/backend/src/data/entities/ApiAuditLogEntry.ts
@@ -0,0 +1,25 @@
+import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn } from "typeorm";
+import { ApiUserInfo } from "./ApiUserInfo";
+import { AuditLogEventData, AuditLogEventType } from "../apiAuditLogTypes";
+
+@Entity("api_audit_log")
+export class ApiAuditLogEntry<TEventType extends AuditLogEventType> {
+  @Column()
+  @PrimaryColumn()
+  id: number;
+
+  @Column()
+  guild_id: string;
+
+  @Column()
+  author_id: string;
+
+  @Column()
+  event_type: TEventType;
+
+  @Column("simple-json")
+  event_data: AuditLogEventData[TEventType];
+
+  @Column()
+  created_at: string;
+}
diff --git a/backend/src/migrations/1630837718830-CreateApiAuditLogTable.ts b/backend/src/migrations/1630837718830-CreateApiAuditLogTable.ts
new file mode 100644
index 00000000..a9f6faea
--- /dev/null
+++ b/backend/src/migrations/1630837718830-CreateApiAuditLogTable.ts
@@ -0,0 +1,58 @@
+import { MigrationInterface, QueryRunner, Table, TableIndex } from "typeorm";
+
+export class CreateApiAuditLogTable1630837718830 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.createTable(
+      new Table({
+        name: "api_audit_log",
+        columns: [
+          {
+            name: "id",
+            type: "int",
+            unsigned: true,
+            isPrimary: true,
+            isGenerated: true,
+            generationStrategy: "increment",
+          },
+          {
+            name: "guild_id",
+            type: "bigint",
+          },
+          {
+            name: "author_id",
+            type: "bigint",
+          },
+          {
+            name: "event_type",
+            type: "varchar",
+            length: "255",
+          },
+          {
+            name: "event_data",
+            type: "longtext",
+          },
+          {
+            name: "created_at",
+            type: "datetime",
+            default: "(NOW())",
+          },
+        ],
+        indices: [
+          new TableIndex({
+            columnNames: ["guild_id", "author_id"],
+          }),
+          new TableIndex({
+            columnNames: ["guild_id", "event_type"],
+          }),
+          new TableIndex({
+            columnNames: ["created_at"],
+          }),
+        ],
+      }),
+    );
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.dropTable("api_audit_log");
+  }
+}