> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.coinflow.cash/guides/payouts/kyc-verification/supported-kyc-methods/reusable-kyc-token-sharing/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.coinflow.cash/_mcp/server. # Reusable KYC Token Sharing ## Overview If you already verify users through Persona or Sumsub, you can share that existing verification with Coinflow instead of asking your users to verify a second time. Reusing the verification gives your users a seamless payout experience and keeps your existing identity infrastructure as the source of truth. ## Who should use this method Reusable KYC token sharing is the right path for merchants who: * Already run an identity verification program through **Persona** or **Sumsub**. * Have users who are already verified through that provider. * Want to avoid asking those users to re-verify with Coinflow. If you don't yet run your own KYC, use [Coinflow's prebuilt KYC](/guides/payouts/kyc-verification/supported-kyc-methods/coinflow-prebuilt-kyc-solution) instead. ## How it works Regardless of which provider you use, the flow is the same: 1. Your existing KYC provider produces a **share token** representing the verified user. 2. You pass that share token to Coinflow's `/withdraw/kyc/share-token` endpoint, along with a `vendor` field identifying the provider. 3. Coinflow ingests the verification data and creates a Withdrawer record. The user can now receive payouts without re-verifying. > **Info** > > **UI integration:** If you use Coinflow's prebuilt withdraw UI (e.g. `CoinflowWithdraw`), call the share-token endpoint **before** rendering the component so the verification record exists when the UI loads. ## Setup The setup steps differ by provider — pick the tab that matches your stack. #### Persona Persona share tokens use Persona's **Connect** data-sharing partnership feature. ### Step 1: Establish a Persona Connect partnership with Coinflow Reach out to a Coinflow integrations representative to set up the [Persona Connect](https://docs.withpersona.com/connect) partnership between your Persona account and Coinflow. You'll receive a Coinflow connection ID once the partnership is approved. ### Step 2: Generate a share token for the verified user Call Persona's [Create Share Token endpoint](https://docs.withpersona.com/creating-share-tokens) to mint a share token for the user's completed Inquiry, referencing the Coinflow connection ID from Step 1: **`Create Share Token`** ```curl Create Share Token curl -X POST https://api.withpersona.com/api/v1/connect/share-tokens \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "data": { "attributes": { "connection-id": "", "source-id": "" } } }' ``` ### Step 3: Register the user with Coinflow Call Coinflow's [Register User Via Share Token endpoint](/api-reference/api-reference/withdraw/create-kyc-share-token) with `vendor: "persona"` and the share token from Step 2. ### Request POST [https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token](https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token) ```curl curl -X POST https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token \ -H "x-coinflow-auth-user-id: " \ -H "Content-Type: application/json" \ -d '{ "vendor": "sumsub", "shareToken": "string", "country": "string", "email": "string" }' ``` ```python import requests url = "https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token" payload = { "vendor": "sumsub", "shareToken": "string", "country": "string", "email": "string" } headers = { "x-coinflow-auth-user-id": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token'; const options = { method: 'POST', headers: {'x-coinflow-auth-user-id': '', 'Content-Type': 'application/json'}, body: '{"vendor":"sumsub","shareToken":"string","country":"string","email":"string"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token" payload := strings.NewReader("{\n \"vendor\": \"sumsub\",\n \"shareToken\": \"string\",\n \"country\": \"string\",\n \"email\": \"string\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("x-coinflow-auth-user-id", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["x-coinflow-auth-user-id"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"vendor\": \"sumsub\",\n \"shareToken\": \"string\",\n \"country\": \"string\",\n \"email\": \"string\"\n}" response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token") .header("x-coinflow-auth-user-id", "") .header("Content-Type", "application/json") .body("{\n \"vendor\": \"sumsub\",\n \"shareToken\": \"string\",\n \"country\": \"string\",\n \"email\": \"string\"\n}") .asString(); ``` ```php request('POST', 'https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token', [ 'body' => '{ "vendor": "sumsub", "shareToken": "string", "country": "string", "email": "string" }', 'headers' => [ 'Content-Type' => 'application/json', 'x-coinflow-auth-user-id' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token"); var request = new RestRequest(Method.POST); request.AddHeader("x-coinflow-auth-user-id", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"vendor\": \"sumsub\",\n \"shareToken\": \"string\",\n \"country\": \"string\",\n \"email\": \"string\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "x-coinflow-auth-user-id": "", "Content-Type": "application/json" ] let parameters = [ "vendor": "sumsub", "shareToken": "string", "country": "string", "email": "string" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Response (200) ```json { "withdrawer": { "_id": "string", "wallet": "string", "blockchain": "solana", "wallets": [ { "wallet": "string", "blockchain": "solana" } ], "email": "string", "availability": { "status": "Functional", "reason": "string", "editor": "string", "updatedAt": "2024-01-15T09:30:00Z" }, "currency": "USD", "merchant": "string", "verification": { "reference": "string", "status": "pending", "vendor": "middesk", "name": "string", "attested": true, "shareToken": "string", "shareTokenStatus": "string", "sessionToken": "string", "rejectionReasons": [ "string" ] }, "riskScoreOverride": true, "country": "string", "user": true, "watchlistExempt": "Unknown", "originalCurrency": "USD", "geoBlockOverride": { "reason": "string", "setBy": "string", "setAt": "2024-01-15T09:30:00Z", "expiresAt": "2024-01-15T09:30:00Z" }, "blockCardReuseExempt": true, "createdAt": "2024-01-15T09:30:00Z", "dwolla": { "customerId": "string", "status": "pending", "acceptedTerms": "2024-01-15T09:30:00Z", "verification": { "reference": "string", "status": "pending", "vendor": "middesk", "name": "string", "attested": true, "shareToken": "string", "shareTokenStatus": "string", "sessionToken": "string", "rejectionReasons": [ "string" ] } }, "watchlistId": "string" } } ``` > **Info** > > When you call [Get Withdrawer](/api-reference/api-reference/withdraw/get-withdrawer) after registration, Coinflow checks the share-token status to confirm the verification is complete before returning the withdrawer record. If Coinflow needs fields your inquiry didn't capture, the user is prompted to fill in the remaining fields. #### Sumsub Sumsub share tokens use Sumsub's **Reusable KYC** feature, which requires a tri-party agreement between you, Sumsub, and Coinflow. ### Step 1: Contact Coinflow Reach out to a Coinflow integrations representative with: * Your Sumsub client ID. * A request for Coinflow's Sumsub client ID (you'll need it when setting up the agreement). ### Step 2: Sign the tri-party agreement Work with Sumsub to establish the data-sharing agreement. This authorizes: * Coinflow to receive KYC data from your Sumsub account. * Reuse of verified user data for payout processing. * Data sharing between you, Sumsub, and Coinflow. [Sumsub: Reusable KYC documentation →](https://docs.sumsub.com/docs/reusable-kyc) ### Step 3: Generate a share token For each verified user, call Sumsub's [Generate Share Token endpoint](https://docs.sumsub.com/api/generate-share-token) to mint a share token. ### Step 4: Register the user with Coinflow Call Coinflow's [Register User Via Share Token endpoint](/api-reference/api-reference/withdraw/create-kyc-share-token) with `vendor: "sumsub"` and the share token from Step 3. ### Request POST [https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token](https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token) ```curl curl -X POST https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token \ -H "x-coinflow-auth-user-id: " \ -H "Content-Type: application/json" \ -d '{ "vendor": "sumsub", "shareToken": "string", "country": "string", "email": "string" }' ``` ```python import requests url = "https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token" payload = { "vendor": "sumsub", "shareToken": "string", "country": "string", "email": "string" } headers = { "x-coinflow-auth-user-id": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token'; const options = { method: 'POST', headers: {'x-coinflow-auth-user-id': '', 'Content-Type': 'application/json'}, body: '{"vendor":"sumsub","shareToken":"string","country":"string","email":"string"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token" payload := strings.NewReader("{\n \"vendor\": \"sumsub\",\n \"shareToken\": \"string\",\n \"country\": \"string\",\n \"email\": \"string\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("x-coinflow-auth-user-id", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["x-coinflow-auth-user-id"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"vendor\": \"sumsub\",\n \"shareToken\": \"string\",\n \"country\": \"string\",\n \"email\": \"string\"\n}" response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token") .header("x-coinflow-auth-user-id", "") .header("Content-Type", "application/json") .body("{\n \"vendor\": \"sumsub\",\n \"shareToken\": \"string\",\n \"country\": \"string\",\n \"email\": \"string\"\n}") .asString(); ``` ```php request('POST', 'https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token', [ 'body' => '{ "vendor": "sumsub", "shareToken": "string", "country": "string", "email": "string" }', 'headers' => [ 'Content-Type' => 'application/json', 'x-coinflow-auth-user-id' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token"); var request = new RestRequest(Method.POST); request.AddHeader("x-coinflow-auth-user-id", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"vendor\": \"sumsub\",\n \"shareToken\": \"string\",\n \"country\": \"string\",\n \"email\": \"string\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "x-coinflow-auth-user-id": "", "Content-Type": "application/json" ] let parameters = [ "vendor": "sumsub", "shareToken": "string", "country": "string", "email": "string" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Response (200) ```json { "withdrawer": { "_id": "string", "wallet": "string", "blockchain": "solana", "wallets": [ { "wallet": "string", "blockchain": "solana" } ], "email": "string", "availability": { "status": "Functional", "reason": "string", "editor": "string", "updatedAt": "2024-01-15T09:30:00Z" }, "currency": "USD", "merchant": "string", "verification": { "reference": "string", "status": "pending", "vendor": "middesk", "name": "string", "attested": true, "shareToken": "string", "shareTokenStatus": "string", "sessionToken": "string", "rejectionReasons": [ "string" ] }, "riskScoreOverride": true, "country": "string", "user": true, "watchlistExempt": "Unknown", "originalCurrency": "USD", "geoBlockOverride": { "reason": "string", "setBy": "string", "setAt": "2024-01-15T09:30:00Z", "expiresAt": "2024-01-15T09:30:00Z" }, "blockCardReuseExempt": true, "createdAt": "2024-01-15T09:30:00Z", "dwolla": { "customerId": "string", "status": "pending", "acceptedTerms": "2024-01-15T09:30:00Z", "verification": { "reference": "string", "status": "pending", "vendor": "middesk", "name": "string", "attested": true, "shareToken": "string", "shareTokenStatus": "string", "sessionToken": "string", "rejectionReasons": [ "string" ] } }, "watchlistId": "string" } } ``` ## Next steps After registering a withdrawer through token sharing, the flow is the same as any other payout: 1. [Get the withdrawer](/api-reference/api-reference/withdraw/get-withdrawer) to confirm `verification.status` is `approved` or `attested`. 2. Add a payout destination (bank account, debit card, IBAN, or PIX) — or use the [Bank Authentication UI](/guides/payouts/implementation-methods/bank-authentication-ui). 3. [Initiate the payout](/api-reference/api-reference/merchant/payout-from-delegated-settlement-wallet). See the full [merchant payout guide](/guides/payouts/source-of-funds-for-payouts/coinflow-wallet) for end-to-end implementation.