diff --git a/regcode.rules.inc b/regcode.rules.inc
index a272870..816c7bf 100644
--- a/regcode.rules.inc
+++ b/regcode.rules.inc
@@ -63,6 +63,65 @@ function regcode_rules_condition_info() {
   );
 }
 
+/**
+ * Implements hook_rules_action_info().
+ */
+function regcode_rules_action_info() {
+  return array(
+    'regcode_rules_action_create' => array(
+      'label' => t('Create registration codes'),
+      'parameter' => array(
+        'quantity' => array(
+          'type' => 'integer',
+          'label' => t('Quantity of registration codes to generate'),
+          'default value' => 1,
+        ),
+        'size' => array(
+          'type' => 'integer',
+          'label' => t('Code size'),
+          'default value' => 10,
+        ),
+        'prefix' => array(
+          'type' => 'text',
+          'label' => t('Code prefix'),
+          'optional' => TRUE,
+        ),
+        'format' => array(
+          'type' => 'text',
+          'label' => t('Format'),
+          'options list' => 'regcode_rules_format_options',
+          'default value' => 'alpha',
+        ),
+        'case' => array(
+          'type' => 'boolean',
+          'label' => t('Uppercase'),
+        ),
+        'maxuses' => array(
+          'type' => 'integer',
+          'label' => t('Maximum uses (0 for unlimited)'),
+          'default value' => 0,
+        ),
+        'begins' => array(
+          'type' => 'date',
+          'label' => t('Active from'),
+          'optional' => TRUE,
+          'default value' => '',
+        ),
+        'expires' => array(
+          'type' => 'date',
+          'label' => t('Expires on'),
+          'optional' => TRUE,
+          'default value' => '',
+        ),
+        'term' => array(
+          'type' => 'taxonomy_term',
+          'label' => t('Tag'),
+        ),
+      ),
+      'group' => t('Registration codes'),
+    ),
+  );
+}
 
 /**
  * Comparison function
@@ -132,3 +191,32 @@ function rules_condition_regcode_code_compare_form($settings, &$form) {
     '#default_value' => $settings['regcode_code'],
   );
 }
+
+
+/**
+ * Returns a list of keys accepted by the $output argument of regcode_generate().
+ */
+function regcode_rules_format_options() {
+  return array(
+    'alpha' => t('Letters'),
+    'numeric' => t('Numbers'),
+    'alphanum' => t('Numbers & Letters'),
+    'hexadec' => t('Hexadecimal'),
+  );
+}
+
+/**
+ * Rules action callback for creating new registration codes.
+ */
+function regcode_rules_action_create($quantity, $size, $prefix, $format, $case, $maxuses, $begins, $expires, $term, $settings) {
+  $code = new stdClass;
+  $code->is_active = 1;
+  $code->maxuses = $maxuses;
+  $code->begins = $begins;
+  $code->expires = $expires;
+
+  for ($i = 0; $i < $quantity; $i++) {
+    $code->code = $prefix . regcode_generate($size, $format, $case);
+    regcode_save($code, array($term->tid => TRUE), REGCODE_MODE_REPLACE);
+  }
+}
