diff --git a/doorman/build.gradle b/doorman/build.gradle
index 457d845d87..b78e7ce979 100644
--- a/doorman/build.gradle
+++ b/doorman/build.gradle
@@ -68,4 +68,11 @@ dependencies {
     testCompile 'junit:junit:4.12'
     testCompile "org.assertj:assertj-core:${assertj_version}"
     testCompile "com.nhaarman:mockito-kotlin:0.6.1"
+
+    compile ('com.atlassian.jira:jira-rest-java-client-core:4.0.0'){
+        // The jira client includes jersey-core 1.5 which breaks everything.
+        exclude module: 'jersey-core'
+    }
+    // Needed by jira rest client
+    compile "com.atlassian.fugue:fugue:2.6.1"
 }
diff --git a/doorman/src/main/kotlin/com/r3/corda/doorman/DoormanParameters.kt b/doorman/src/main/kotlin/com/r3/corda/doorman/DoormanParameters.kt
index adee96471f..805dc10d06 100644
--- a/doorman/src/main/kotlin/com/r3/corda/doorman/DoormanParameters.kt
+++ b/doorman/src/main/kotlin/com/r3/corda/doorman/DoormanParameters.kt
@@ -1,6 +1,7 @@
 package com.r3.corda.doorman
 
 import com.r3.corda.doorman.OptionParserHelper.toConfigWithOptions
+import com.typesafe.config.Config
 import net.corda.core.div
 import net.corda.node.services.config.ConfigHelper
 import net.corda.node.services.config.getOrElse
@@ -14,7 +15,6 @@ class DoormanParameters(args: Array<String>) {
         accepts("basedir", "Overriding configuration filepath, default to current directory.").withRequiredArg().describedAs("filepath")
         accepts("keygen", "Generate CA keypair and certificate using provide Root CA key.").withOptionalArg()
         accepts("rootKeygen", "Generate Root CA keypair and certificate.").withOptionalArg()
-        accepts("approveAll", "Approve all certificate signing request.").withOptionalArg()
         accepts("keystorePath", "CA keystore filepath, default to [basedir]/certificates/caKeystore.jks.").withRequiredArg().describedAs("filepath")
         accepts("rootStorePath", "Root CA keystore filepath, default to [basedir]/certificates/rootCAKeystore.jks.").withRequiredArg().describedAs("filepath")
         accepts("keystorePassword", "CA keystore password.").withRequiredArg().describedAs("password")
@@ -32,10 +32,10 @@ class DoormanParameters(args: Array<String>) {
     val caPrivateKeyPassword: String? by config.getOrElse { null }
     val rootKeystorePassword: String? by config.getOrElse { null }
     val rootPrivateKeyPassword: String? by config.getOrElse { null }
-    val approveAll: Boolean by config.getOrElse { false }
     val host: String by config
     val port: Int by config
-    val dataSourceProperties: Properties by  config
+    val dataSourceProperties: Properties by config
+    val jiraConfig = if (config.hasPath("jiraConfig")) JiraConfig(config.getConfig("jiraConfig")) else null
     private val keygen: Boolean by config.getOrElse { false }
     private val rootKeygen: Boolean by config.getOrElse { false }
 
@@ -44,6 +44,12 @@ class DoormanParameters(args: Array<String>) {
     enum class Mode {
         DOORMAN, CA_KEYGEN, ROOT_KEYGEN
     }
+
+    class JiraConfig(config: Config) {
+        val address: String by config
+        val projectCode: String by config
+        val username: String by config
+        val password: String by config
+        val doneTransitionCode: Int by config
+    }
 }
-
-
diff --git a/doorman/src/main/kotlin/com/r3/corda/doorman/Main.kt b/doorman/src/main/kotlin/com/r3/corda/doorman/Main.kt
index bd51d30a75..93e0b6ec89 100644
--- a/doorman/src/main/kotlin/com/r3/corda/doorman/Main.kt
+++ b/doorman/src/main/kotlin/com/r3/corda/doorman/Main.kt
@@ -1,9 +1,11 @@
 package com.r3.corda.doorman
 
+import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory
 import com.google.common.net.HostAndPort
 import com.r3.corda.doorman.DoormanServer.Companion.logger
 import com.r3.corda.doorman.persistence.CertificationRequestStorage
 import com.r3.corda.doorman.persistence.DBCertificateRequestStorage
+import com.r3.corda.doorman.persistence.JiraCertificateRequestStorage
 import net.corda.core.createDirectories
 import net.corda.core.crypto.X509Utilities
 import net.corda.core.crypto.X509Utilities.CACertAndKey
@@ -13,6 +15,7 @@ import net.corda.core.crypto.X509Utilities.CORDA_ROOT_CA
 import net.corda.core.crypto.X509Utilities.CORDA_ROOT_CA_PRIVATE_KEY
 import net.corda.core.crypto.X509Utilities.addOrReplaceKey
 import net.corda.core.crypto.X509Utilities.createIntermediateCert
+import net.corda.core.crypto.X509Utilities.createServerCert
 import net.corda.core.crypto.X509Utilities.loadCertificateAndKey
 import net.corda.core.crypto.X509Utilities.loadKeyStore
 import net.corda.core.crypto.X509Utilities.loadOrCreateKeyStore
@@ -31,6 +34,7 @@ import org.glassfish.jersey.servlet.ServletContainer
 import java.io.Closeable
 import java.lang.Thread.sleep
 import java.net.InetSocketAddress
+import java.net.URI
 import java.security.cert.Certificate
 import kotlin.concurrent.thread
 import kotlin.system.exitProcess
@@ -159,22 +163,33 @@ private fun DoormanParameters.startDoorman() {
     val caCertAndKey = X509Utilities.loadCertificateAndKey(keystore, caPrivateKeyPassword, CORDA_INTERMEDIATE_CA_PRIVATE_KEY)
     // Create DB connection.
     val (datasource, database) = configureDatabase(dataSourceProperties)
-    val storage = DBCertificateRequestStorage(database)
-    // Daemon thread approving all request periodically.
-    if (approveAll) {
-        thread(name = "Request Approval Daemon", isDaemon = true) {
-            logger.warn("Doorman server is in 'Approve All' mode, this will approve all incoming certificate signing request.")
-            while (true) {
-                sleep(10.seconds.toMillis())
-                for (id in storage.getPendingRequestIds()) {
-                    storage.approveRequest(id, {
-                        JcaPKCS10CertificationRequest(it.request).run {
-                            X509Utilities.createServerCert(subject, publicKey, caCertAndKey,
-                                    if (it.ipAddress == it.hostName) listOf() else listOf(it.hostName), listOf(it.ipAddress))
-                        }
-                    })
-                    logger.info("Approved request $id")
+
+    val requestStorage = DBCertificateRequestStorage(database)
+
+    val storage = if (jiraConfig == null) {
+        logger.warn("Doorman server is in 'Approve All' mode, this will approve all incoming certificate signing request.")
+        // Approve all pending request.
+        object : CertificationRequestStorage by requestStorage {
+            // The doorman is in approve all mode, returns all pending request id as approved request id.
+            override fun getApprovedRequestIds() = getPendingRequestIds()
+        }
+    } else {
+        val jiraClient = AsynchronousJiraRestClientFactory().createWithBasicHttpAuthentication(URI(jiraConfig.address), jiraConfig.username, jiraConfig.password)
+        JiraCertificateRequestStorage(requestStorage, jiraClient, jiraConfig.projectCode, jiraConfig.doneTransitionCode)
+    }
+
+    // Daemon thread approving request periodically.
+    thread(name = "Request Approval Daemon") {
+        while (true) {
+            sleep(10.seconds.toMillis())
+            // TODO: Handle rejected request?
+            for (id in storage.getApprovedRequestIds()) {
+                storage.approveRequest(id) {
+                    val request = JcaPKCS10CertificationRequest(request)
+                    createServerCert(request.subject, request.publicKey, caCertAndKey,
+                            if (ipAddress == hostName) listOf() else listOf(hostName), listOf(ipAddress))
                 }
+                logger.info("Approved request $id")
             }
         }
     }
diff --git a/doorman/src/main/kotlin/com/r3/corda/doorman/persistence/CertificationRequestStorage.kt b/doorman/src/main/kotlin/com/r3/corda/doorman/persistence/CertificationRequestStorage.kt
index 0e9db59c0f..3f63756f20 100644
--- a/doorman/src/main/kotlin/com/r3/corda/doorman/persistence/CertificationRequestStorage.kt
+++ b/doorman/src/main/kotlin/com/r3/corda/doorman/persistence/CertificationRequestStorage.kt
@@ -26,7 +26,7 @@ interface CertificationRequestStorage {
     /**
      * Approve the given request by generating and storing a new certificate using the provided generator.
      */
-    fun approveRequest(requestId: String, certificateGenerator: (CertificationRequestData) -> Certificate)
+    fun approveRequest(requestId: String, generateCertificate: CertificationRequestData.() -> Certificate)
 
     /**
      * Reject the given request using the given reason.
@@ -35,9 +35,13 @@ interface CertificationRequestStorage {
 
     /**
      * Retrieve list of request IDs waiting for approval.
-     * TODO : This is used for the background thread to approve request automatically without KYC checks, should be removed after testnet.
      */
     fun getPendingRequestIds(): List<String>
+
+    /**
+     * Retrieve list of approved request IDs.
+     */
+    fun getApprovedRequestIds(): List<String>
 }
 
 data class CertificationRequestData(val hostName: String, val ipAddress: String, val request: PKCS10CertificationRequest)
diff --git a/doorman/src/main/kotlin/com/r3/corda/doorman/persistence/DBCertificateRequestStorage.kt b/doorman/src/main/kotlin/com/r3/corda/doorman/persistence/DBCertificateRequestStorage.kt
index 1bd0809655..62282b1254 100644
--- a/doorman/src/main/kotlin/com/r3/corda/doorman/persistence/DBCertificateRequestStorage.kt
+++ b/doorman/src/main/kotlin/com/r3/corda/doorman/persistence/DBCertificateRequestStorage.kt
@@ -83,13 +83,13 @@ class DBCertificateRequestStorage(private val database: Database) : Certificatio
         }
     }
 
-    override fun approveRequest(requestId: String, certificateGenerator: (CertificationRequestData) -> Certificate) {
+    override fun approveRequest(requestId: String, generateCertificate: CertificationRequestData.() -> Certificate) {
         databaseTransaction(database) {
             val request = singleRequestWhere { DataTable.requestId eq requestId and DataTable.processTimestamp.isNull() }
             if (request != null) {
                 withFinalizables { finalizables ->
                     DataTable.update({ DataTable.requestId eq requestId }) {
-                        it[certificate] = serializeToBlob(certificateGenerator(request), finalizables)
+                        it[certificate] = serializeToBlob(request.generateCertificate(), finalizables)
                         it[processTimestamp] = Instant.now()
                     }
                 }
@@ -121,6 +121,8 @@ class DBCertificateRequestStorage(private val database: Database) : Certificatio
         }
     }
 
+    override fun getApprovedRequestIds(): List<String> = emptyList()
+
     private fun singleRequestWhere(where: SqlExpressionBuilder.() -> Op<Boolean>): CertificationRequestData? {
         return DataTable
                 .select(where)
diff --git a/doorman/src/main/kotlin/com/r3/corda/doorman/persistence/JiraCertificateRequestStorage.kt b/doorman/src/main/kotlin/com/r3/corda/doorman/persistence/JiraCertificateRequestStorage.kt
new file mode 100644
index 0000000000..bca5678d43
--- /dev/null
+++ b/doorman/src/main/kotlin/com/r3/corda/doorman/persistence/JiraCertificateRequestStorage.kt
@@ -0,0 +1,76 @@
+package com.r3.corda.doorman.persistence
+
+import com.atlassian.jira.rest.client.api.JiraRestClient
+import com.atlassian.jira.rest.client.api.domain.Field
+import com.atlassian.jira.rest.client.api.domain.IssueType
+import com.atlassian.jira.rest.client.api.domain.input.IssueInputBuilder
+import com.atlassian.jira.rest.client.api.domain.input.TransitionInput
+import net.corda.core.crypto.X509Utilities
+import net.corda.core.crypto.commonName
+import net.corda.core.utilities.loggerFor
+import org.bouncycastle.asn1.x500.style.BCStyle
+import org.bouncycastle.openssl.jcajce.JcaPEMWriter
+import org.bouncycastle.util.io.pem.PemObject
+import java.io.StringWriter
+import java.security.cert.Certificate
+
+class JiraCertificateRequestStorage(val delegate: CertificationRequestStorage,
+                                    val jiraClient: JiraRestClient,
+                                    val projectCode: String,
+                                    val doneTransitionCode: Int) : CertificationRequestStorage by delegate {
+    private enum class Status {
+        Approved, Rejected
+    }
+
+    companion object {
+        private val logger = loggerFor<JiraCertificateRequestStorage>()
+    }
+
+    // The JIRA project must have a Request ID field and the Task issue type.
+    private val requestIdField: Field = jiraClient.metadataClient.fields.claim().find { it.name == "Request ID" }!!
+    private val taskIssueType: IssueType = jiraClient.metadataClient.issueTypes.claim().find { it.name == "Task" }!!
+
+    override fun saveRequest(certificationData: CertificationRequestData): String {
+        val requestId = delegate.saveRequest(certificationData)
+        // Make sure request has been accepted.
+        val response = getResponse(requestId)
+        if (response !is CertificateResponse.Unauthorised) {
+            val request = StringWriter()
+            JcaPEMWriter(request).use {
+                it.writeObject(PemObject("CERTIFICATE REQUEST", certificationData.request.encoded))
+            }
+            val commonName = certificationData.request.subject.commonName
+            val email = certificationData.request.subject.getRDNs(BCStyle.EmailAddress).firstOrNull()?.first?.value
+            val nearestCity = certificationData.request.subject.getRDNs(BCStyle.L).firstOrNull()?.first?.value
+
+            val issue = IssueInputBuilder().setIssueTypeId(taskIssueType.id)
+                    .setProjectKey(projectCode)
+                    .setDescription("Legal Name: $commonName\nNearest City: $nearestCity\nEmail: $email\n\n{code}$request{code}")
+                    .setSummary(commonName)
+                    .setFieldValue(requestIdField.id, requestId)
+            // This will block until the issue is created.
+            jiraClient.issueClient.createIssue(issue.build()).fail { logger.error("Exception when creating JIRA issue.", it) }.claim()
+        }
+        return requestId
+    }
+
+    override fun approveRequest(requestId: String, generateCertificate: CertificationRequestData.() -> Certificate) {
+        delegate.approveRequest(requestId, generateCertificate)
+        // Certificate should be created, retrieving it to attach to the jira task.
+        val certificate = (getResponse(requestId) as? CertificateResponse.Ready)?.certificate
+        // Jira only support ~ (contains) search for custom textfield.
+        val issue = jiraClient.searchClient.searchJql("'Request ID' ~ $requestId").claim().issues.firstOrNull()
+        if (issue != null) {
+            jiraClient.issueClient.transition(issue, TransitionInput(doneTransitionCode)).fail { logger.error("Exception when transiting JIRA status.", it) }.claim()
+            jiraClient.issueClient.addAttachment(issue.attachmentsUri, certificate?.encoded?.inputStream(), "${X509Utilities.CORDA_CLIENT_CA}.cer")
+                    .fail { logger.error("Exception when uploading attachment to JIRA.", it) }.claim()
+        }
+    }
+
+    override fun getApprovedRequestIds(): List<String> = getRequestByStatus(Status.Approved)
+
+    private fun getRequestByStatus(status: Status): List<String> {
+        val issues = jiraClient.searchClient.searchJql("project = $projectCode AND status = $status").claim().issues
+        return issues.map { it.getField(requestIdField.id)?.value?.toString() }.filterNotNull()
+    }
+}
diff --git a/doorman/src/main/resources/reference.conf b/doorman/src/main/resources/reference.conf
index 446362dba0..dcf1ccff1c 100644
--- a/doorman/src/main/resources/reference.conf
+++ b/doorman/src/main/resources/reference.conf
@@ -3,7 +3,6 @@ port = 0
 keystorePath = ${basedir}"/certificates/caKeystore.jks"
 keystorePassword = "password"
 caPrivateKeyPassword = "password"
-approveAll = true
 
 dataSourceProperties {
   dataSourceClassName = org.h2.jdbcx.JdbcDataSource
@@ -11,4 +10,12 @@ dataSourceProperties {
   "dataSource.user" = sa
   "dataSource.password" = ""
 }
-h2port = 0
\ No newline at end of file
+h2port = 0
+
+jiraConfig{
+  address = "https://doorman-jira-host/"
+  projectCode = "TD"
+  username = "username"
+  password = "password"
+  doneTransitionCode = 41
+}
diff --git a/doorman/src/test/kotlin/com/r3/corda/doorman/DoormanParametersTest.kt b/doorman/src/test/kotlin/com/r3/corda/doorman/DoormanParametersTest.kt
index 7767d010c0..2f53e80e02 100644
--- a/doorman/src/test/kotlin/com/r3/corda/doorman/DoormanParametersTest.kt
+++ b/doorman/src/test/kotlin/com/r3/corda/doorman/DoormanParametersTest.kt
@@ -7,11 +7,10 @@ import kotlin.test.assertTrue
 class DoormanParametersTest {
     @Test
     fun `parse arg correctly`() {
-        val params = DoormanParameters(arrayOf("--keygen", "--keystorePath", "./testDummyPath.jks", "--approveAll"))
+        val params = DoormanParameters(arrayOf("--keygen", "--keystorePath", "./testDummyPath.jks"))
         assertEquals(DoormanParameters.Mode.CA_KEYGEN, params.mode)
         assertEquals("./testDummyPath.jks", params.keystorePath.toString())
         assertEquals(0, params.port)
-        assertTrue(params.approveAll)
 
         val params2 = DoormanParameters(arrayOf("--keystorePath", "./testDummyPath.jks", "--port", "1000"))
         assertEquals(DoormanParameters.Mode.DOORMAN, params2.mode)
diff --git a/doorman/src/test/kotlin/com/r3/corda/doorman/DoormanServiceTest.kt b/doorman/src/test/kotlin/com/r3/corda/doorman/DoormanServiceTest.kt
index 6ad0fb07d4..314ba29945 100644
--- a/doorman/src/test/kotlin/com/r3/corda/doorman/DoormanServiceTest.kt
+++ b/doorman/src/test/kotlin/com/r3/corda/doorman/DoormanServiceTest.kt
@@ -86,9 +86,9 @@ class DoormanServiceTest {
         assertThat(pollForResponse(id)).isEqualTo(PollResponse.NotReady)
 
         storage.approveRequest(id) {
-            JcaPKCS10CertificationRequest(it.request).run {
+            JcaPKCS10CertificationRequest(request).run {
                 X509Utilities.createServerCert(subject, publicKey, intermediateCA,
-                        if (it.ipAddress == it.hostName) listOf() else listOf(it.hostName), listOf(it.ipAddress))
+                        if (ipAddress == hostName) listOf() else listOf(hostName), listOf(ipAddress))
             }
         }
 
diff --git a/doorman/src/test/kotlin/com/r3/corda/doorman/internal/persistence/DBCertificateRequestStorageTest.kt b/doorman/src/test/kotlin/com/r3/corda/doorman/internal/persistence/DBCertificateRequestStorageTest.kt
index 6a84306b33..6b0593441a 100644
--- a/doorman/src/test/kotlin/com/r3/corda/doorman/internal/persistence/DBCertificateRequestStorageTest.kt
+++ b/doorman/src/test/kotlin/com/r3/corda/doorman/internal/persistence/DBCertificateRequestStorageTest.kt
@@ -133,14 +133,14 @@ class DBCertificateRequestStorageTest {
 
     private fun approveRequest(requestId: String) {
         storage.approveRequest(requestId) {
-            JcaPKCS10CertificationRequest(it.request).run {
+            JcaPKCS10CertificationRequest(request).run {
                 X509Utilities.createServerCert(
                         subject,
                         publicKey,
                         intermediateCA,
-                        if (it.ipAddress == it.hostName) listOf() else listOf(it.hostName),
-                        listOf(it.ipAddress))
+                        if (ipAddress == hostName) listOf() else listOf(hostName),
+                        listOf(ipAddress))
             }
         }
     }
-}
\ No newline at end of file
+}