diff --git a/CHANGES b/CHANGES
index cbc1b03cd2b24f1d8f726918fcd00b2de874ccb1..37144e0513cc10f4d9a407299fba450267d2ee82 100644
--- a/CHANGES
+++ b/CHANGES
@@ -277,6 +277,8 @@ CDR
  * Multiple files and formats can now be specified in cdr_custom.conf.
  * cdr_syslog has been added which allows CDRs to be written directly to syslog.
    See configs/cdr_syslog.conf.sample for more information.
+ * A 'sequence' field has been added to CDRs which can be combined with
+   linkedid or uniqueid to uniquely identify a CDR.
 
 Calendaring for Asterisk
 ------------------------
diff --git a/apps/app_forkcdr.c b/apps/app_forkcdr.c
index 4bacbef78c2b16f0b615b3ba806d6c6e67f4faf5..12d5049140c8e9a3fffacba7a70ff3d663e2a9b2 100644
--- a/apps/app_forkcdr.c
+++ b/apps/app_forkcdr.c
@@ -184,7 +184,7 @@ static void ast_cdr_fork(struct ast_channel *chan, struct ast_flags optflags, ch
 	while (cdr->next)
 		cdr = cdr->next;
 	
-	if (!(newcdr = ast_cdr_dup(cdr)))
+	if (!(newcdr = ast_cdr_dup_unique(cdr)))
 		return;
 	
 	ast_cdr_append(cdr, newcdr);
diff --git a/configs/cdr_custom.conf.sample b/configs/cdr_custom.conf.sample
index 4a533a462b7c1bae3d0026d3139eb2187752df02..ce3248b5dcc2da8331e5878d8d215dd1d61a3430 100644
--- a/configs/cdr_custom.conf.sample
+++ b/configs/cdr_custom.conf.sample
@@ -7,6 +7,6 @@
 ; Master.csv, Simple.csv, or both.
 ;
 ;[mappings]
-;Master.csv => ${CSV_QUOTE(${CDR(clid)})},${CSV_QUOTE(${CDR(src)})},${CSV_QUOTE(${CDR(dst)})},${CSV_QUOTE(${CDR(dcontext)})},${CSV_QUOTE(${CDR(channel)})},${CSV_QUOTE(${CDR(dstchannel)})},${CSV_QUOTE(${CDR(lastapp)})},${CSV_QUOTE(${CDR(lastdata)})},${CSV_QUOTE(${CDR(start)})},${CSV_QUOTE(${CDR(answer)})},${CSV_QUOTE(${CDR(end)})},${CSV_QUOTE(${CDR(duration)})},${CSV_QUOTE(${CDR(billsec)})},${CSV_QUOTE(${CDR(disposition)})},${CSV_QUOTE(${CDR(amaflags)})},${CSV_QUOTE(${CDR(accountcode)})},${CSV_QUOTE(${CDR(uniqueid)})},${CSV_QUOTE(${CDR(userfield)})}
+;Master.csv => ${CSV_QUOTE(${CDR(clid)})},${CSV_QUOTE(${CDR(src)})},${CSV_QUOTE(${CDR(dst)})},${CSV_QUOTE(${CDR(dcontext)})},${CSV_QUOTE(${CDR(channel)})},${CSV_QUOTE(${CDR(dstchannel)})},${CSV_QUOTE(${CDR(lastapp)})},${CSV_QUOTE(${CDR(lastdata)})},${CSV_QUOTE(${CDR(start)})},${CSV_QUOTE(${CDR(answer)})},${CSV_QUOTE(${CDR(end)})},${CSV_QUOTE(${CDR(duration)})},${CSV_QUOTE(${CDR(billsec)})},${CSV_QUOTE(${CDR(disposition)})},${CSV_QUOTE(${CDR(amaflags)})},${CSV_QUOTE(${CDR(accountcode)})},${CSV_QUOTE(${CDR(uniqueid)})},${CSV_QUOTE(${CDR(userfield)})},${CDR(sequence)}
 ;Simple.csv => ${CSV_QUOTE(${EPOCH})},${CSV_QUOTE(${CDR(src)})},${CSV_QUOTE(${CDR(dst)})}
 
diff --git a/funcs/func_cdr.c b/funcs/func_cdr.c
index e7f14af2a9a30d596302fa54db5a0cc117447818..d8a3283fc934f9e34f9b10eb464be43f8173d968 100644
--- a/funcs/func_cdr.c
+++ b/funcs/func_cdr.c
@@ -99,6 +99,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 					<enum name="channel">
 						<para>Channel name.</para>
 					</enum>
+					<enum name="sequence">
+						<para>CDR sequence number.</param>
+					</enum>
 				</enumlist>
 			</parameter>
 			<parameter name="options" required="false">
diff --git a/include/asterisk/cdr.h b/include/asterisk/cdr.h
index 9ac0e114771c5806a11b412ad18a36375c0e99d4..23c63ea503e0df9f6cdf268a02e64fc669a5fe49 100644
--- a/include/asterisk/cdr.h
+++ b/include/asterisk/cdr.h
@@ -110,6 +110,8 @@ struct ast_cdr {
 	char linkedid[32];
 	/*! User field */
 	char userfield[AST_MAX_USER_FIELD];
+	/*! Sequence field */
+	int sequence;
 
 	/*! A linked list for variables */
 	struct varshead varshead;
@@ -142,10 +144,38 @@ int check_cdr_enabled(void);
  */
 struct ast_cdr *ast_cdr_alloc(void);
 
+/*! 
+ * \brief Duplicate a record and increment the sequence number.
+ * \param cdr the record to duplicate
+ * \retval a malloc'd ast_cdr structure, 
+ * \retval NULL on error (malloc failure)
+ * \see ast_cdr_dup()
+ * \see ast_cdr_dup_unique_swap()
+ */
+struct ast_cdr *ast_cdr_dup_unique(struct ast_cdr *cdr);
+
+/*! 
+ * \brief Duplicate a record and increment the sequence number of the old
+ * record.
+ * \param cdr the record to duplicate
+ * \retval a malloc'd ast_cdr structure, 
+ * \retval NULL on error (malloc failure)
+ * \note This version increments the original CDR's sequence number rather than
+ * the duplicate's sequence number. The effect is as if the original CDR's
+ * sequence number was swapped with the duplicate's sequence number.
+ *
+ * \see ast_cdr_dup()
+ * \see ast_cdr_dup_unique()
+ */
+struct ast_cdr *ast_cdr_dup_unique_swap(struct ast_cdr *cdr);
+
 /*! 
  * \brief Duplicate a record 
+ * \param cdr the record to duplicate
  * \retval a malloc'd ast_cdr structure, 
  * \retval NULL on error (malloc failure)
+ * \see ast_cdr_dup_unique()
+ * \see ast_cdr_dup_unique_swap()
  */
 struct ast_cdr *ast_cdr_dup(struct ast_cdr *cdr);
 
diff --git a/main/cdr.c b/main/cdr.c
index 69d4a8fe15c21590f2538b4d632bf6ce5cde646d..9168978a4193d361ddeaafc8217642e1ae4889a1 100644
--- a/main/cdr.c
+++ b/main/cdr.c
@@ -74,6 +74,11 @@ static struct ast_cdr_batch {
 	struct ast_cdr_batch_item *tail;
 } *batch = NULL;
 
+
+static int cdr_sequence =  0;
+
+static int cdr_seq_inc(struct ast_cdr *cdr);
+
 static struct sched_context *sched;
 static int cdr_sched = -1;
 static pthread_t cdr_thread = AST_PTHREADT_NULL;
@@ -165,6 +170,26 @@ int ast_cdr_isset_unanswered(void)
 	return unanswered;
 }
 
+struct ast_cdr *ast_cdr_dup_unique(struct ast_cdr *cdr) 
+{
+	struct ast_cdr *newcdr = ast_cdr_dup(cdr);
+	if (!newcdr)
+		return NULL;
+
+	cdr_seq_inc(newcdr);
+	return newcdr;
+}
+
+struct ast_cdr *ast_cdr_dup_unique_swap(struct ast_cdr *cdr) 
+{
+	struct ast_cdr *newcdr = ast_cdr_dup(cdr);
+	if (!newcdr)
+		return NULL;
+
+	cdr_seq_inc(cdr);
+	return newcdr;
+}
+
 /*! Duplicate a CDR record 
 	\returns Pointer to new CDR record
 */
@@ -279,6 +304,8 @@ void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *wor
 		ast_copy_string(workspace, cdr->linkedid, workspacelen);
 	else if (!strcasecmp(name, "userfield"))
 		ast_copy_string(workspace, cdr->userfield, workspacelen);
+	else if (!strcasecmp(name, "sequence"))
+		snprintf(workspace, workspacelen, "%d", cdr->sequence);
 	else if ((varbuf = ast_cdr_getvar_internal(cdr, name, recur)))
 		ast_copy_string(workspace, varbuf, workspacelen);
 	else
@@ -292,7 +319,7 @@ void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *wor
 static const char * const cdr_readonly_vars[] = { "clid", "src", "dst", "dcontext", "channel", "dstchannel",
 						  "lastapp", "lastdata", "start", "answer", "end", "duration",
 						  "billsec", "disposition", "amaflags", "accountcode", "uniqueid", "linkedid",
-						  "userfield", NULL };
+						  "userfield", "sequence", NULL };
 /*! Set a CDR channel variable 
 	\note You can't set the CDR variables that belong to the actual CDR record, like "billsec".
 */
@@ -847,6 +874,11 @@ int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
 	return 0;
 }
 
+static int cdr_seq_inc(struct ast_cdr *cdr)
+{
+	return (cdr->sequence = ast_atomic_fetchadd_int(&cdr_sequence, +1));
+}
+
 int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
 {
 	char *chan;
@@ -856,6 +888,7 @@ int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
 			chan = S_OR(cdr->channel, "<unknown>");
 			ast_copy_string(cdr->channel, c->name, sizeof(cdr->channel));
 			set_one_cid(cdr, c);
+			cdr_seq_inc(cdr);
 
 			cdr->disposition = (c->_state == AST_STATE_UP) ?  AST_CDR_ANSWERED : AST_CDR_NOANSWER;
 			cdr->amaflags = c->amaflags ? c->amaflags :  ast_default_amaflags;
@@ -1116,7 +1149,7 @@ void ast_cdr_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
 		if (ast_test_flag(&flags, AST_CDR_FLAG_LOCKED) || !ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
 			if (ast_test_flag(&flags, AST_CDR_FLAG_POSTED)) {
 				ast_cdr_end(cdr);
-				if ((duplicate = ast_cdr_dup(cdr))) {
+				if ((duplicate = ast_cdr_dup_unique_swap(cdr))) {
 					ast_cdr_detach(duplicate);
 				}
 				ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
diff --git a/main/features.c b/main/features.c
index 132281c5b0edd0834750dc3bcc82035b6c3bb7fa..cfce3f1d7e7790861d2ed8a330155a92f2c8d32a 100644
--- a/main/features.c
+++ b/main/features.c
@@ -2804,7 +2804,7 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
 		if (chan_cdr) {
 			ast_set_flag(chan_cdr, AST_CDR_FLAG_MAIN);
 			ast_cdr_update(chan);
-			bridge_cdr = ast_cdr_dup(chan_cdr);
+			bridge_cdr = ast_cdr_dup_unique_swap(chan_cdr);
 			ast_copy_string(bridge_cdr->lastapp, S_OR(chan->appl, ""), sizeof(bridge_cdr->lastapp));
 			ast_copy_string(bridge_cdr->lastdata, S_OR(chan->data, ""), sizeof(bridge_cdr->lastdata));
 		} else {