> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.coinflow.cash/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 <YOUR_PERSONA_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "attributes": {
        "connection-id": "<COINFLOW_CONNECTION_ID>",
        "source-id": "<INQUIRY_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: <apiKey>" \
     -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": "<apiKey>",
    "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': '<apiKey>', '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", "<apiKey>")
	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"] = '<apiKey>'
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<String> response = Unirest.post("https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token")
  .header("x-coinflow-auth-user-id", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"vendor\": \"sumsub\",\n  \"shareToken\": \"string\",\n  \"country\": \"string\",\n  \"email\": \"string\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->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' => '<apiKey>',
  ],
]);

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", "<apiKey>");
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": "<apiKey>",
  "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: <apiKey>" \
     -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": "<apiKey>",
    "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': '<apiKey>', '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", "<apiKey>")
	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"] = '<apiKey>'
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<String> response = Unirest.post("https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token")
  .header("x-coinflow-auth-user-id", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"vendor\": \"sumsub\",\n  \"shareToken\": \"string\",\n  \"country\": \"string\",\n  \"email\": \"string\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->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' => '<apiKey>',
  ],
]);

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", "<apiKey>");
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": "<apiKey>",
  "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.