diff --git a/app/models/badge.rb b/app/models/badge.rb
index 9db8b37b0f8..b94e24e9bff 100644
--- a/app/models/badge.rb
+++ b/app/models/badge.rb
@@ -10,6 +10,7 @@ class Badge < ActiveRecord::Base
   FirstLike = 11
   FirstShare = 12
   FirstFlag = 13
+  FirstLink = 14
 
   # other consts
   AutobiographerMinBioLength = 10
@@ -17,6 +18,20 @@ class Badge < ActiveRecord::Base
 
   module Queries
 
+    FirstLink = <<SQL
+    SELECT l.user_id, l.post_id, l.created_at granted_at
+    FROM
+    (
+      SELECT MIN(l1.id) id
+      FROM topic_links l1
+      JOIN badge_posts p1 ON p1.id = l1.post_id
+      JOIN badge_posts p2 ON p2.id = l1.link_post_id
+      WHERE NOT reflection AND p1.topic_id <> p2.topic_id
+      GROUP BY l1.user_id
+    ) ids
+    JOIN topic_links l ON l.id = ids.id
+SQL
+
     FirstShare = <<SQL
     SELECT views.user_id, p2.id post_id, i2.created_at granted_at
     FROM
@@ -24,11 +39,8 @@ class Badge < ActiveRecord::Base
       SELECT i.user_id, MIN(i.id) i_id
       FROM incoming_links i
       JOIN topics t on t.id = i.topic_id
-      JOIN posts p on p.topic_id = t.id AND p.post_number = i.post_number
-      WHERE i.user_id IS NOT NULL AND
-            p.deleted_at IS NULL AND
-            t.deleted_at IS NULL AND
-            t.visible
+      JOIN badge_posts p on p.topic_id = t.id AND p.post_number = i.post_number
+      WHERE i.user_id IS NOT NULL
       GROUP BY i.user_id
     ) as views
     JOIN incoming_links i2 ON i2.id = views.i_id
@@ -38,47 +50,31 @@ SQL
     FirstFlag = <<SQL
     SELECT pa.user_id, min(pa.created_at) granted_at
     FROM post_actions pa
-    JOIN posts p on p.id = pa.post_id
-    JOIN topics t on t.id = p.topic_id
-    WHERE p.deleted_at IS NULL AND
-          t.deleted_at IS NULL AND
-          t.visible AND
-          post_action_type_id IN (#{PostActionType.flag_types.values.join(",")})
+    JOIN badge_posts p on p.id = pa.post_id
+    WHERE post_action_type_id IN (#{PostActionType.flag_types.values.join(",")})
     GROUP BY pa.user_id
 SQL
 
     FirstLike = <<SQL
     SELECT pa.user_id, min(post_id) post_id, min(pa.created_at) granted_at
     FROM post_actions pa
-    JOIN posts p on p.id = pa.post_id
-    JOIN topics t on t.id = p.topic_id
-    WHERE p.deleted_at IS NULL AND
-          t.deleted_at IS NULL AND
-          t.visible AND
-          post_action_type_id = 2
+    JOIN badge_posts p on p.id = pa.post_id
+    WHERE post_action_type_id = 2
     GROUP BY pa.user_id
 SQL
 
     Editor = <<SQL
     SELECT p.user_id, min(p.id) post_id, min(p.created_at) granted_at
-    FROM posts p
-    JOIN topics t on t.id = p.topic_id
-    WHERE p.deleted_at IS NULL AND
-          t.deleted_at IS NULL AND
-          t.visible AND
-          p.self_edits > 0
+    FROM badge_posts p
+    WHERE p.self_edits > 0
     GROUP BY p.user_id
 SQL
 
     Welcome = <<SQL
     SELECT p.user_id, min(post_id) post_id, min(pa.created_at) granted_at
     FROM post_actions pa
-    JOIN posts p on p.id = pa.post_id
-    JOIN topics t on t.id = p.topic_id
-    WHERE p.deleted_at IS NULL AND
-          t.deleted_at IS NULL AND
-          t.visible AND
-          post_action_type_id = 2
+    JOIN badge_posts p on p.id = pa.post_id
+    WHERE post_action_type_id = 2
     GROUP BY p.user_id
 SQL
 
@@ -93,12 +89,9 @@ SQL
     def self.like_badge(count)
       # we can do better with dates, but its hard work
 "
-    SELECT p.user_id, p.id post_id, p.updated_at granted_at FROM posts p
-    JOIN topics t on p.topic_id = t.id
-    WHERE p.deleted_at IS NULL AND
-          t.deleted_at IS NULL AND
-          t.visible AND
-          p.like_count >= #{count.to_i}
+    SELECT p.user_id, p.id post_id, p.updated_at granted_at
+    FROM badge_posts p
+    WHERE p.like_count >= #{count.to_i}
 "
     end
 
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 27beafe6500..7cf00fd293b 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -1987,3 +1987,6 @@ en:
         first_share:
           name: First Share
           description: Shared a post
+        first_link:
+          name: First Link
+          description: Added an internal link to another topic
diff --git a/db/fixtures/006_badges.rb b/db/fixtures/006_badges.rb
index 0af9adc9617..929913a46a8 100644
--- a/db/fixtures/006_badges.rb
+++ b/db/fixtures/006_badges.rb
@@ -18,6 +18,15 @@ trust_level_badges.each do |spec|
   end
 end
 
+Badge.seed do |b|
+  b.id = Badge::FirstLink
+  b.name = "First Link"
+  b.badge_type_id = BadgeType::Bronze
+  b.multiple_grant = false
+  b.target_posts = true
+  b.query = Badge::Queries::FirstLink
+end
+
 Badge.seed do |b|
   b.id = Badge::FirstLike
   b.name = "First Like"
diff --git a/db/migrate/20140710005023_add_badge_posts_and_topics_view.rb b/db/migrate/20140710005023_add_badge_posts_and_topics_view.rb
new file mode 100644
index 00000000000..5f1cae360ef
--- /dev/null
+++ b/db/migrate/20140710005023_add_badge_posts_and_topics_view.rb
@@ -0,0 +1,20 @@
+class AddBadgePostsAndTopicsView < ActiveRecord::Migration
+  def up
+    add_column :categories, :allow_badges, :boolean, default: true, null: false
+
+    execute "CREATE VIEW badge_posts AS
+    SELECT p.*
+    FROM posts p
+    JOIN topics t ON t.id = p.topic_id
+    JOIN categories c ON c.id = t.category_id
+    WHERE c.allow_badges AND
+          p.deleted_at IS NULL AND
+          t.deleted_at IS NULL AND
+          t.visible"
+  end
+
+  def down
+    execute "DROP VIEW badge_posts"
+    remove_column :categories, :allow_badges
+  end
+end