> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.tabs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.tabs.com/_mcp/server.

# Bulk link payment accounts

POST https://integrators.prod.api.tabsplatform.com/v3/payment-accounts/bulk-link
Content-Type: application/json

Points a batch of your Tabs customers at payment-processor customer accounts that already exist — a Stripe `cus_…` id for each. Useful when migrating customers you already bill through Stripe onto Tabs.

This only records the link. It never creates a customer in Stripe: if the reference does not already exist there, the link is still written, but nothing will charge against it.

Send at most 100 entries per request and split larger imports across several calls. Every entry is checked against your merchant account first, and each one is reported back individually — the response returns one result per entry, in the order sent, so a rejected entry does not hide behind a successful batch. A customer that is not yours comes back as `NOT_FOUND` and is left untouched.

**Default payment method.** Pass `defaultPaymentMethodId` to choose the default yourself. Leave it out and Tabs reads the customer's current default from the processor instead — that read runs asynchronously, just after this call returns, so the `defaultPaymentMethod` in the response is only what Tabs has on record at that moment and is usually empty for a newly linked account. It fills in shortly afterwards on its own; re-read the customer a little later rather than treating the response as final. There is no status endpoint for that follow-up work.

**Re-linking.** Sending a different reference for a customer that is already linked repoints them to the new one. The previously stored default payment method is cleared and resolved again for the new account.

Each entry is applied independently: one failure does not roll back the entries that already succeeded.

Reference: https://docs.tabs.com/api-reference/payment-accounts/integrators-api-payment-accounts-controller-v-3-bulk-link-payment-accounts

## Authentication

- `Authorization` header (required)

## Request

### Body (application/json)

- `items` (list of object, required) — The accounts to link. At most 100 per request; split larger imports across several calls.
  - `customerId` (string, required) — Tabs customer id to link the processor account to.
  - `processorType` (enum, required) — Payment processor the account lives on. `STRIPE` is the only supported value.
    - Allowed values: `STRIPE`
  - `processorCustomerRef` (string, required) — The processor's own customer identifier — for Stripe, the `cus_…` id of a customer that already exists in your Stripe account.
  - `defaultPaymentMethodId` (string, optional) — Optional payment method to set as this customer's default — for Stripe, a `pm_…` id belonging to the customer above. Leave it out and Tabs reads the customer's current default straight from the processor instead. That read happens asynchronously, shortly after this call returns, so the `defaultPaymentMethod` in this response is whatever Tabs has on record right now — usually empty for a newly linked account. It fills in on its own; re-read the customer a little later to see it. If the customer has no default at the processor, it stays empty.

## Response

### 202

Accepted. One result per request entry: `LINKED`, `NOT_FOUND` (not your customer), or `FAILED` with a reason.

- `payload` (object, required) — Response payload, will be empty when success is false
  - `items` (list of object, required) — One result per request entry, in the order they were sent.
    - `customerId` (string, required) — Tabs customer id from the matching request entry.
    - `processorType` (enum, required) — Processor from the matching request entry.
      - Allowed values: `STRIPE`
    - `status` (enum, required) — `LINKED` — the account is now linked. `NOT_FOUND` — no such customer under your merchant, nothing was changed. `FAILED` — the link was attempted and rejected; see `error`.
      - Allowed values: `LINKED`, `NOT_FOUND`, `FAILED`
    - `processorCustomerRef` (string, optional) — The linked processor reference. Present when `status` is `LINKED`.
    - `defaultPaymentMethod` (string, optional, nullable) — Default payment method Tabs currently has on record. Empty on a freshly linked account until the asynchronous processor read completes — see `defaultPaymentMethodId` on the request.
    - `defaultPaymentMethodType` (enum, optional, nullable) — Type of `defaultPaymentMethod`.
      - Allowed values: `CREDIT_CARD`, `ACH_DEBIT`, `ACH_CREDIT`, `CHECK`, `LINK`
    - `isDefaultProcessor` (boolean, optional) — Whether this processor is your merchant account's default.
    - `error` (string, optional) — Why the entry was rejected. Present when `status` is `FAILED`.
- `success` (boolean, required) — Boolean with true=success, false=failure
- `message` (string, required) — Plain-text description of the result
- `error` (object, optional) — json element with any error messages or warnings
  - `code` (double, required) — API response code
  - `message` (string, required) — API response message
  - `details` (object, optional) — Additional details about the error

## Examples

**Request**

```json
{
  "items": [
    {
      "customerId": "123e4567-e89b-12d3-a456-426614174000",
      "processorType": "STRIPE",
      "processorCustomerRef": "cus_NffrFeUfNV2Hib"
    }
  ]
}
```

**Response**

```json
{
  "payload": {
    "items": [
      {
        "customerId": "string",
        "processorType": "STRIPE",
        "status": "LINKED",
        "processorCustomerRef": "string",
        "defaultPaymentMethod": "string",
        "defaultPaymentMethodType": "CREDIT_CARD",
        "isDefaultProcessor": true,
        "error": "string"
      }
    ]
  },
  "success": true,
  "message": "string",
  "error": {
    "code": 1.1,
    "message": "string",
    "details": {}
  }
}
```

**SDK Code**

```python
import requests

url = "https://integrators.prod.api.tabsplatform.com/v3/payment-accounts/bulk-link"

payload = { "items": [
        {
            "customerId": "123e4567-e89b-12d3-a456-426614174000",
            "processorType": "STRIPE",
            "processorCustomerRef": "cus_NffrFeUfNV2Hib"
        }
    ] }
headers = {
    "Authorization": "<apiKey>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://integrators.prod.api.tabsplatform.com/v3/payment-accounts/bulk-link';
const options = {
  method: 'POST',
  headers: {Authorization: '<apiKey>', 'Content-Type': 'application/json'},
  body: '{"items":[{"customerId":"123e4567-e89b-12d3-a456-426614174000","processorType":"STRIPE","processorCustomerRef":"cus_NffrFeUfNV2Hib"}]}'
};

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://integrators.prod.api.tabsplatform.com/v3/payment-accounts/bulk-link"

	payload := strings.NewReader("{\n  \"items\": [\n    {\n      \"customerId\": \"123e4567-e89b-12d3-a456-426614174000\",\n      \"processorType\": \"STRIPE\",\n      \"processorCustomerRef\": \"cus_NffrFeUfNV2Hib\"\n    }\n  ]\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "<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://integrators.prod.api.tabsplatform.com/v3/payment-accounts/bulk-link")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"items\": [\n    {\n      \"customerId\": \"123e4567-e89b-12d3-a456-426614174000\",\n      \"processorType\": \"STRIPE\",\n      \"processorCustomerRef\": \"cus_NffrFeUfNV2Hib\"\n    }\n  ]\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://integrators.prod.api.tabsplatform.com/v3/payment-accounts/bulk-link")
  .header("Authorization", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"items\": [\n    {\n      \"customerId\": \"123e4567-e89b-12d3-a456-426614174000\",\n      \"processorType\": \"STRIPE\",\n      \"processorCustomerRef\": \"cus_NffrFeUfNV2Hib\"\n    }\n  ]\n}")
  .asString();
```

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

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://integrators.prod.api.tabsplatform.com/v3/payment-accounts/bulk-link', [
  'body' => '{
  "items": [
    {
      "customerId": "123e4567-e89b-12d3-a456-426614174000",
      "processorType": "STRIPE",
      "processorCustomerRef": "cus_NffrFeUfNV2Hib"
    }
  ]
}',
  'headers' => [
    'Authorization' => '<apiKey>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://integrators.prod.api.tabsplatform.com/v3/payment-accounts/bulk-link");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"items\": [\n    {\n      \"customerId\": \"123e4567-e89b-12d3-a456-426614174000\",\n      \"processorType\": \"STRIPE\",\n      \"processorCustomerRef\": \"cus_NffrFeUfNV2Hib\"\n    }\n  ]\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = ["items": [
    [
      "customerId": "123e4567-e89b-12d3-a456-426614174000",
      "processorType": "STRIPE",
      "processorCustomerRef": "cus_NffrFeUfNV2Hib"
    ]
  ]] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://integrators.prod.api.tabsplatform.com/v3/payment-accounts/bulk-link")! 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()
```