diff --git a/hasura/metadata/actions.graphql b/hasura/metadata/actions.graphql
index ab91ccf751e71d5c94e753da09e49315eb5851be..2d49d28b67547d8c9f5c15736fc777d1164fe8f8 100644
--- a/hasura/metadata/actions.graphql
+++ b/hasura/metadata/actions.graphql
@@ -1,3 +1,13 @@
+type Mutation {
+  addTransaction(
+    id: String!
+    address: String!
+    hash: String!
+    signature: String!
+    comment: String!
+  ): AddTransactionResponse
+}
+
 type Mutation {
   deleteProfile(
     address: String!
@@ -54,3 +64,8 @@ type MigrateProfileResponse {
   message: String!
 }
 
+type AddTransactionResponse {
+  success: Boolean!
+  message: String!
+}
+
diff --git a/hasura/metadata/actions.yaml b/hasura/metadata/actions.yaml
index a067b7d3e240ef2f65cc1a687ef391b9fcd05640..b03cfce6a65fb6beb9c2f1615410f6efe8b35c51 100644
--- a/hasura/metadata/actions.yaml
+++ b/hasura/metadata/actions.yaml
@@ -1,4 +1,11 @@
 actions:
+  - name: addTransaction
+    definition:
+      kind: synchronous
+      handler: http://host.docker.internal:3000/add-transaction
+    permissions:
+      - role: public
+    comment: addTransaction
   - name: deleteProfile
     definition:
       kind: synchronous
@@ -30,4 +37,5 @@ custom_types:
     - name: UpdateProfileResponse
     - name: DeleteProfileResponse
     - name: MigrateProfileResponse
+    - name: AddTransactionResponse
   scalars: []
diff --git a/hasura/metadata/databases/default/tables/public_transactions.yaml b/hasura/metadata/databases/default/tables/public_transactions.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..e8bbb909d53726dc2b0841687c5b79089bc3e446
--- /dev/null
+++ b/hasura/metadata/databases/default/tables/public_transactions.yaml
@@ -0,0 +1,3 @@
+table:
+  name: transactions
+  schema: public
diff --git a/hasura/metadata/databases/default/tables/tables.yaml b/hasura/metadata/databases/default/tables/tables.yaml
index 6dc41508ffaf12842ee7663763870f4b4529fc01..722f2fa78476e35a7eb5b79f6798d3abd92a725b 100644
--- a/hasura/metadata/databases/default/tables/tables.yaml
+++ b/hasura/metadata/databases/default/tables/tables.yaml
@@ -1 +1,2 @@
 - "!include public_profiles.yaml"
+- "!include public_transactions.yaml"
diff --git a/hasura/migrations/default/1705109757208_add-transactions/up.sql b/hasura/migrations/default/1705109757208_add-transactions/up.sql
new file mode 100644
index 0000000000000000000000000000000000000000..a72e2ae984343f43b77881e8a9930018f7ee0889
--- /dev/null
+++ b/hasura/migrations/default/1705109757208_add-transactions/up.sql
@@ -0,0 +1,29 @@
+SET check_function_bodies = false;
+CREATE TABLE public.profiles (
+    address text NOT NULL,
+    avatar bytea,
+    description text,
+    geoloc point,
+    title text,
+    city text,
+    socials jsonb,
+    created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
+    updated_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP
+);
+CREATE FUNCTION public.bytea_to_base64(data_row public.profiles) RETURNS text
+    LANGUAGE plpgsql STABLE
+    AS $$
+BEGIN
+    RETURN ENCODE(data_row.avatar, 'base64');
+END;
+$$;
+CREATE TABLE public.transactions (
+    id text NOT NULL,
+    comment text NOT NULL,
+    created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP
+);
+COMMENT ON TABLE public.transactions IS 'Store transactions comments';
+ALTER TABLE ONLY public.profiles
+    ADD CONSTRAINT profiles_pkey PRIMARY KEY (address);
+ALTER TABLE ONLY public.transactions
+    ADD CONSTRAINT transactions_pkey PRIMARY KEY (id);
diff --git a/index.ts b/index.ts
index 09f50263c11888116d10028771f258dff86e57a7..37865a6cf7532aab91082ebd9787ae5cbe65de6f 100644
--- a/index.ts
+++ b/index.ts
@@ -15,6 +15,7 @@ import {
 } from "./lib/utils.ts";
 import ApiDuniter from "./lib/duniter_connect.ts";
 import { DuniterService } from "./lib/duniter_service.ts";
+import { addTransaction } from "./lib/add_transaction.ts";
 
 // Determine the environment
 const isProduction = Deno.env.get("PRODUCTION") === "true";
@@ -84,6 +85,10 @@ router.post(
   "/migrate-profile-data",
   async (ctx: Context) => await migrateProfile(ctx, client),
 );
+router.post(
+  "/add-transaction",
+  async (ctx: Context) => await addTransaction(ctx, client),
+);
 
 app.use(router.routes());
 app.use(router.allowedMethods());
diff --git a/lib/add_transaction.ts b/lib/add_transaction.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bfc3dfface847ce9e70dbf5f91f460e256efe66a
--- /dev/null
+++ b/lib/add_transaction.ts
@@ -0,0 +1,89 @@
+import { Context } from "https://deno.land/x/oak@v12.6.1/context.ts";
+import { Client } from "https://deno.land/x/postgres@v0.17.0/client.ts";
+import {
+  SignatureResponse,
+  signatureResponseMessages,
+  verifySignature,
+} from "./signature_verify.ts";
+import { Transaction } from "./types.ts";
+import { checkRecordExist } from "./utils.ts";
+
+export async function addTransaction(ctx: Context, client: Client) {
+  try {
+    const body = await ctx.request.body().value;
+    const transaction: Transaction = body.variables || body.input || {};
+
+    // Validate input
+    if (!transaction.id || !transaction.comment) {
+      ctx.response.status = 400;
+      ctx.response.body = {
+        success: false,
+        message: "transaction id and comment are required.",
+      };
+      return;
+    }
+
+    // Verify signature
+    const signatureResult = await verifySignature(transaction);
+    if (signatureResult !== SignatureResponse.valid) {
+      ctx.response.status = 400;
+      console.error(
+        "Invalid signature: " + signatureResponseMessages[signatureResult],
+      );
+      ctx.response.body = {
+        success: false,
+        message: "Invalid signature: " +
+          signatureResponseMessages[signatureResult],
+      };
+      return;
+    }
+
+    // Verify new profile doesn't exists
+    if (await checkRecordExist(client, 'transactions', 'id', transaction.id)) {
+      ctx.response.status = 422;
+      console.error(`ID ${transaction.id} already exist.`);
+      ctx.response.body = {
+        success: false,
+        message: `ID ${transaction.id} already exist.`,
+      };
+      return;
+    }
+
+    // Prepare and execute database query for deletion
+    const addTransactionQuery = `
+    INSERT INTO transactions (id, comment)
+    VALUES ($1, $2);
+    `;
+
+
+    try {
+      await client.queryObject({
+        text: addTransactionQuery,
+        args: [transaction.id, transaction.comment],
+      });
+      ctx.response.status = 200;
+      console.log(
+        `Transaction with ID ${transaction.id} has been insert.`,
+      );
+      ctx.response.body = {
+        success: true,
+        message:
+          `Transaction with ID ${transaction.id} has been insert.`,
+      };
+    } catch (error) {
+      console.error("Database error in insert transaction:", error);
+      ctx.response.status = 500;
+      ctx.response.body = {
+        success: false,
+        message: "Internal server error: " + error,
+      };
+    }
+  } catch (error) {
+    console.error("Error insert transaction:", error);
+    ctx.response.status = 500;
+    ctx.response.body = {
+      success: false,
+      message: "Error insert transaction: " + error,
+    };
+  }
+}
diff --git a/lib/delete_profile.ts b/lib/delete_profile.ts
index e91f904ad1d1d3f2baebfebfedd53062fd3807e2..1de78447aaa8622d6b013a6c3d298c4b744859fa 100644
--- a/lib/delete_profile.ts
+++ b/lib/delete_profile.ts
@@ -2,7 +2,7 @@ import { Context } from "https://deno.land/x/oak@v12.6.1/context.ts";
 import { Client } from "https://deno.land/x/postgres@v0.17.0/client.ts";
 import { Profile } from "./types.ts";
 import { SignatureResponse, verifySignature } from "./signature_verify.ts";
-import { checkProfileExist } from "./utils.ts";
+import { checkRecordExist } from "./utils.ts";
 
 export async function deleteProfile(ctx: Context, client: Client) {
   try {
@@ -22,7 +22,7 @@ export async function deleteProfile(ctx: Context, client: Client) {
     }
 
     // Verify if profile exists
-    if (!await checkProfileExist(client, profile.address)) {
+    if (!await checkRecordExist(client, 'profiles', 'address', profile.address)) {
       ctx.response.status = 404;
       console.error(`Profile ${profile.address} does not exist.`);
       ctx.response.body = {
diff --git a/lib/duniter_service.ts b/lib/duniter_service.ts
index 133cab9a7d34bb42aad49486ec75574883904d6d..fdbf0c28977546f649b7dadd75d5d6634b65ea90 100644
--- a/lib/duniter_service.ts
+++ b/lib/duniter_service.ts
@@ -3,7 +3,7 @@ import { ApiPromise } from "https://deno.land/x/polkadot@0.2.45/api/mod.ts";
 import { type EventRecord } from "https://deno.land/x/polkadot@0.2.45/types/interfaces/system/index.ts";
 import { BN } from "https://deno.land/x/polkadot@0.2.45/util/bn/index.ts";
 import { Client } from "https://deno.land/x/postgres@v0.17.0/client.ts";
-import { checkProfileExist } from "./utils.ts";
+import { checkRecordExist } from "./utils.ts";
 
 export class DuniterService {
   private api!: ApiPromise;
@@ -98,7 +98,7 @@ export class DuniterService {
           const killedAddress = event.event.data[0].toString();
 
           // Verify if profile exists
-          if (!await checkProfileExist(client, killedAddress)) {
+          if (!await checkRecordExist(client, 'profiles', 'address', killedAddress)) {
             console.log(`Profile ${killedAddress} does not exist.`);
             return;
           }
diff --git a/lib/migrate_profile.ts b/lib/migrate_profile.ts
index 3db3ec3e3fc83c56b9b30285b00f359d5269e7ac..64a6315874b1a5f1ef063754c0d48e743d18e2d2 100644
--- a/lib/migrate_profile.ts
+++ b/lib/migrate_profile.ts
@@ -6,7 +6,7 @@ import {
   signatureResponseMessages,
   verifySignature,
 } from "./signature_verify.ts";
-import { checkProfileExist } from "./utils.ts";
+import { checkRecordExist } from "./utils.ts";
 
 export async function migrateProfile(ctx: Context, client: Client) {
   try {
@@ -53,7 +53,7 @@ export async function migrateProfile(ctx: Context, client: Client) {
     }
 
     // Verify old profile exists
-    if (!await checkProfileExist(client, profile.oldAddress)) {
+    if (!await checkRecordExist(client, 'profiles', 'address', profile.oldAddress)) {
       ctx.response.status = 404;
       console.error(`Profile ${profile.oldAddress} does not exist.`);
       ctx.response.body = {
@@ -64,7 +64,7 @@ export async function migrateProfile(ctx: Context, client: Client) {
     }
 
     // Verify new profile doesn't exists
-    if (await checkProfileExist(client, profile.address)) {
+    if (await checkRecordExist(client, 'profiles', 'address', profile.address)) {
       ctx.response.status = 422;
       console.error(`Profile ${profile.address} already exist.`);
       ctx.response.body = {
diff --git a/lib/signature_verify.ts b/lib/signature_verify.ts
index eb618897dd14dbdea7146ba39b0cf011fb3073c2..f511b137fe713f7d00d8bb938d36d0cf18aad9aa 100644
--- a/lib/signature_verify.ts
+++ b/lib/signature_verify.ts
@@ -2,7 +2,7 @@ import {
   base64Decode,
   signatureVerify,
 } from "https://deno.land/x/polkadot@0.2.45/util-crypto/mod.ts";
-import { Profile } from "./types.ts";
+import { Profile, Transaction } from "./types.ts";
 
 export enum SignatureResponse {
   valid,
@@ -21,7 +21,7 @@ export const signatureResponseMessages: { [key in SignatureResponse]: string } =
   };
 
 export async function verifySignature(
-  profile: Profile,
+  profile: Profile | Transaction,
 ): Promise<SignatureResponse> {
   let payload: string;
   let addressSign: string;
@@ -42,6 +42,16 @@ export async function verifySignature(
       address,
     });
     addressSign = oldAddress!;
+  // If comment is present, then we are on a add transaction event
+  } else if ("comment" in profile) {
+    const { address, id, comment } = profile;
+    payload = JSON.stringify({
+      id,
+      address,
+      comment,
+    });
+    addressSign = address!;
+    // Else we are on an update or delete profilie event
   } else {
     const { address, description, avatarBase64, geoloc, title, city, socials } =
       profile;
diff --git a/lib/types.ts b/lib/types.ts
index fd6a481fc494fc1ef6a9bd02e8803aaea4dd6380..f6d4b5684acc79ab1633f5a3f81236da4a93648b 100644
--- a/lib/types.ts
+++ b/lib/types.ts
@@ -13,3 +13,11 @@ export type Profile = {
   socials?: SocialInfo[];
   oldAddress?: string;
 };
+
+export type Transaction = {
+  id: string;
+  address: string;
+  comment: string;
+  hash: string;
+  signature: string;
+}
diff --git a/lib/update_profile.ts b/lib/update_profile.ts
index 299f8e3b6d363801186e7a0cb423cc54d0ab6133..e7143ecdea09726336991376d6c8181dd13ec2aa 100644
--- a/lib/update_profile.ts
+++ b/lib/update_profile.ts
@@ -1,7 +1,7 @@
 import { Context } from "https://deno.land/x/oak@v12.6.1/context.ts";
 import { Client } from "https://deno.land/x/postgres@v0.17.0/client.ts";
 import { SignatureResponse, verifySignature } from "./signature_verify.ts";
-import { checkProfileExist, convertBase64ToBytea } from "./utils.ts";
+import { checkRecordExist, convertBase64ToBytea } from "./utils.ts";
 import { Profile } from "./types.ts";
 import { DuniterService } from "./duniter_service.ts";
 
@@ -26,7 +26,7 @@ export async function updateProfile(ctx: Context, client: Client) {
     // Verify wallet exist in blockchain
     const balanceService = new DuniterService();
     if (
-      !await checkProfileExist(client, profile.address) &&
+      !await checkRecordExist(client, 'profiles', 'address', profile.address) &&
       await balanceService.getBalance(profile.address) === 0
     ) {
       ctx.response.status = 400;
diff --git a/lib/utils.ts b/lib/utils.ts
index d1b9c8451bfc232099debdabb90fe82f3cb385c8..dca2e06dc3a1aa8a71158e1e4110e3d02e781c42 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -99,15 +99,18 @@ export async function waitForTableCreation(
   throw new Error(`Table ${tableName} not found after ${maxAttempts} try.`);
 }
 
-export async function checkProfileExist(
+export async function checkRecordExist(
   client: Client,
-  address: string,
+  tableName: string,
+  columnName: string,
+  value: string
 ): Promise<boolean> {
-  const query = `SELECT 1 FROM profiles WHERE address = $1;`;
+  const query = `SELECT 1 FROM ${tableName} WHERE ${columnName} = $1;`;
   const result = await client.queryObject({
     text: query,
-    args: [address],
+    args: [value],
   });
   const rowCount = result.rowCount ?? 0;
   return rowCount > 0;
 }
+
diff --git a/scripts/export-migrations.sh b/scripts/export-migrations.sh
index 1388a6598088cb9bd85d6d2b8c85bdfa0990bdf5..db40ee0b18e148a74807aa4435776c071dcca994 100755
--- a/scripts/export-migrations.sh
+++ b/scripts/export-migrations.sh
@@ -3,7 +3,10 @@
 export $(cat .env | grep -E 'HASURA_GRAPHQL_ADMIN_SECRET|HASURA_LISTEN_PORT')
 endpoint="http://localhost:$HASURA_LISTEN_PORT"
 
-hasura migrate create "init" --from-server --endpoint $endpoint --admin-secret $HASURA_GRAPHQL_ADMIN_SECRET --database-name default
+name=$1
+[[ ! $name ]] && echo "Please set a name for this migration" && exit 1
+
+hasura migrate create $name --from-server --endpoint $endpoint --admin-secret $HASURA_GRAPHQL_ADMIN_SECRET --database-name default
 
 # To manually apply saved migrations:
 # hasura migrate apply --endpoint $endpoint --admin-secret $HASURA_GRAPHQL_ADMIN_SECRET --database-name default
\ No newline at end of file