> For the complete documentation index, see [llms.txt](https://developers.bloomcredit.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.bloomcredit.io/consumer-permissioned-data/bring-your-own-token/appending-processor-tokens.md).

# Appending Processor Tokens

How to use PATCH /v2/invite-tokens/processor-tokens

## What is this?

After users & processor tokens have been registered via `POST /v2/invite-tokens`, you might need to add more processor tokens to *unclaimed* emails or email + invite code pairs using [Append processor tokens to an existing invite](/reference/consumer-permissioned-data/append-processor-tokens-to-an-existing-invite.md).

For example:

* You created an invite token for `alice@example.com` with one Plaid processor token representing her checking account.
* Later, Alice links her savings account too. You now have a second processor token for her. Alice has not registered for Bloom+ yet.
* Instead of deleting and recreating the invite token, you use this endpoint to append the new processor token to Alice's existing invite.

This can be done for multiple emails at once — up to 100 emails in a single request.

> **Note:** This endpoint only works for organizations that have processor tokens enabled. If your organization only uses invite codes (no processor tokens), this endpoint will return an error.

***

## Quick start

### Append one token to one email

{% code title="Shell" %}

```bash
curl -X PATCH https://<your-bloom-api-host>/v2/invite-tokens/processor-tokens \
  -H "Content-Type: application/json" \
  -H "x-partner: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -d '{
    "items": [
      {
        "email": "alice@example.com",
        "processor_tokens": ["processor-sandbox-new-token"]
      }
    ]
  }'
```

{% endcode %}

### Append multiple tokens to multiple emails

{% code title="Shell" %}

```bash
curl -X PATCH https://<your-bloom-api-host>/v2/invite-tokens/processor-tokens \
  -H "Content-Type: application/json" \
  -H "x-partner: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -d '{
    "items": [
      {
        "email": "alice@example.com",
        "processor_tokens": [
          "processor-sandbox-alice-savings",
          "processor-sandbox-alice-investment"
        ]
      },
      {
        "email": "bob@example.com",
        "processor_tokens": [
          "processor-sandbox-bob-savings"
        ]
      }
    ]
  }'
```

{% endcode %}

> Replace `<your-bloom-api-host>` with the base URL provided by your Bloom representative. Replace the `x-partner` UUID with your actual organization ID.

***

## Before you begin

### 1. You need existing invite tokens

This endpoint **adds** processor tokens to existing invite tokens. It does not create new invite tokens. If the email doesn't already have an invite token, the request will fail for that email.

To create invite tokens in the first place, use the batch create endpoint:

```
POST /v2/invite-tokens
```

See the [Batch Invite Tokens](/consumer-permissioned-data/bring-your-own-token.md) documentation for details.

### 2. The invite token must be unused and not expired

You can only append processor tokens to invite tokens that:

* Have **not been used** yet (the user hasn't enrolled)
* Have **not expired** (the expiration date hasn't passed)

If the invite token has already been used or has expired, the request will fail for that email.

### 3. Your organization ID (`x-partner`)

This is a UUID that identifies your organization, in UUID format. For example:

```
a1b2c3d4-e5f6-7890-abcd-ef1234567890
```

If you don't have your organization ID, reach out to your Bloom representative.

<br>

### What's the difference between this endpoint and `POST /v2/invite-tokens`?

|                                     | `POST /v2/invite-tokens` | `PATCH /v2/invite-tokens/processor-tokens`     |
| ----------------------------------- | ------------------------ | ---------------------------------------------- |
| **Purpose**                         | Create new invite tokens | Add processor tokens to existing invite tokens |
| **Creates invite tokens?**          | Yes                      | No                                             |
| **Generates invite codes?**         | Yes (if enabled)         | No                                             |
| **Requires existing invite token?** | No                       | Yes                                            |
| **Sets expiration?**                | Yes                      | No (uses the existing expiration)              |

***

## Bloom API call

```
PATCH /v2/invite-tokens/processor-tokens
```

Appends processor tokens to multiple existing invite tokens at once. Requires authentication via the `x-partner` header.

API reference: [Append processor tokens to an existing invite](/reference/consumer-permissioned-data/append-processor-tokens-to-an-existing-invite.md)

### Headers

| Header         | Value                  | Description                             |
| -------------- | ---------------------- | --------------------------------------- |
| `x-partner`    | Your organization UUID | Identifies your organization. Required. |
| `Content-Type` | `application/json`     | Always `application/json`. Required.    |

If `x-partner` is missing, invalid, or doesn't match a known organization, you will get a `401 Unauthorized` error.

### Request body

{% code title="JSON" %}

```json
{
  "items": [
    {
      "email": "alice@example.com",
      "processor_tokens": ["processor-sandbox-new-token-1"]
    },
    {
      "email": "bob@example.com",
      "processor_tokens": ["processor-sandbox-new-token-2", "processor-sandbox-new-token-3"]
    }
  ]
}
```

{% endcode %}

| Field   | Type  | Required | Description                                                                   |
| ------- | ----- | -------- | ----------------------------------------------------------------------------- |
| `items` | array | Yes      | List of emails with processor tokens to append. Min: 1, Max: 100 per request. |

Each item in the `items` array:

| Field              | Type      | Required | Description                                                                     |
| ------------------ | --------- | -------- | ------------------------------------------------------------------------------- |
| `email`            | string    | Yes      | The email address of the existing invite token. Max 254 characters.             |
| `processor_tokens` | string\[] | Yes      | New processor tokens to add. Min: 1, Max: 25 per email, each max 255 characters |

### Example

Suppose Alice already has an invite token with one processor token (`existing-checking-token`), and you want to add a second one for her savings account.

**Request:**

{% code title="JSON" %}

```json
{
  "items": [
    {
      "email": "alice@example.com",
      "processor_tokens": ["new-savings-token"]
    }
  ]
}
```

{% endcode %}

**Response:**

{% code title="JSON" %}

```json
{
  "success_count": 1,
  "succeeded": [
    {
      "email": "alice@example.com",
      "processor_tokens": ["existing-checking-token", "new-savings-token"]
    }
  ],
  "failed": []
}
```

{% endcode %}

> Notice that `processor_tokens` in the response includes **all** tokens — both the one Alice already had (`existing-checking-token`) and the one you just added (`new-savings-token`). You always get the full picture back.

That's it. You send the email and the new tokens, and you get back the complete list of everything that's now associated with that invite.

### Response

Every response has the same shape, regardless of how many items succeeded or failed:

{% code title="JSON" %}

```json
{
  "success_count": 1,
  "succeeded": [ ... ],
  "failed": [ ... ]
}
```

{% endcode %}

| Field           | Type    | Description                                                             |
| --------------- | ------- | ----------------------------------------------------------------------- |
| `success_count` | integer | How many emails were successfully updated.                              |
| `succeeded`     | array   | List of emails that were updated, with their full processor token list. |
| `failed`        | array   | List of emails that were NOT updated, with reasons.                     |

> **Note:** `succeeded` and `failed` are always JSON arrays, never `null`. If all items succeed, `failed` will be `[]`. If all fail, `succeeded` will be `[]`.
>
> **Note:** The response preserves the order of the input. If you sent `[alice, bob, charlie]`, the response items will be in the same order across both `succeeded` and `failed`.

Each item in `succeeded`:

| Field              | Type      | Description                                                                                                   |
| ------------------ | --------- | ------------------------------------------------------------------------------------------------------------- |
| `email`            | string    | The email address.                                                                                            |
| `processor_tokens` | string\[] | The **full** list of all processor tokens now associated with this invite, including both old and new tokens. |

Each item in `failed`:

| Field   | Type   | Description                            |
| ------- | ------ | -------------------------------------- |
| `email` | string | The email address that failed.         |
| `error` | string | A human-readable reason why it failed. |

<br>

### After the request

Compare `success_count` against the number of items you sent — did they all go through?

Check the `processor_tokens` array in each `succeeded` item — it shows the **full** list of tokens after the append, so you can confirm the new ones were added.

Check `failed` for any items that didn't go through. Read the `error` field to understand why and fix accordingly.

**Do not assume everything succeeded just because the HTTP status is 200.** Always check both `succeeded` and `failed`.

***

<br>

### Example: All succeed

You already created invite tokens for Alice and Bob with one processor token each. Now you're adding a second token to each.

**Request:**

{% code title="JSON" %}

```json
{
  "items": [
    {
      "email": "alice@example.com",
      "processor_tokens": ["processor-sandbox-alice-savings"]
    },
    {
      "email": "bob@example.com",
      "processor_tokens": ["processor-sandbox-bob-savings"]
    }
  ]
}
```

{% endcode %}

**Response:**

{% code title="JSON" %}

```json
{
  "success_count": 2,
  "succeeded": [
    {
      "email": "alice@example.com",
      "processor_tokens": ["processor-sandbox-alice-checking", "processor-sandbox-alice-savings"]
    },
    {
      "email": "bob@example.com",
      "processor_tokens": ["processor-sandbox-bob-checking", "processor-sandbox-bob-savings"]
    }
  ],
  "failed": []
}
```

{% endcode %}

Notice that `processor_tokens` in the response includes **all** tokens — both the original ones (`*-checking`) and the newly appended ones (`*-savings`).

***

### Example: Partial success

Alice's append works, but Bob's fails because he doesn't have an invite token yet.

**Response:**

{% code title="JSON" %}

```json
{
  "success_count": 1,
  "succeeded": [
    {
      "email": "alice@example.com",
      "processor_tokens": ["processor-sandbox-alice-checking", "processor-sandbox-alice-savings"]
    }
  ],
  "failed": [
    {
      "email": "bob@example.com",
      "error": "no unused, non-expired invite token found for email"
    }
  ]
}
```

{% endcode %}

***

### Example: Tokens already associated

You try to append `"tok-1"` to Alice, but she already has `"tok-1"`. This is not an error — it's a no-op. The response shows success and returns her full token list.

**Request:**

{% code title="JSON" %}

```json
{
  "items": [
    {
      "email": "alice@example.com",
      "processor_tokens": ["tok-1"]
    }
  ]
}
```

{% endcode %}

**Response:**

{% code title="JSON" %}

```json
{
  "success_count": 1,
  "succeeded": [
    {
      "email": "alice@example.com",
      "processor_tokens": ["tok-1", "tok-2"]
    }
  ],
  "failed": []
}
```

{% endcode %}

Alice already had `tok-1` and `tok-2`. You tried to add `tok-1` again. Nothing changed, and the response shows her full list.

***

### Example: Processor token conflict

You try to append `"tok-xyz"` to Alice, but `"tok-xyz"` is already used by someone else (maybe in a different organization). Alice's entire item fails.

**Response:**

{% code title="JSON" %}

```json
{
  "success_count": 0,
  "succeeded": [],
  "failed": [
    {
      "email": "alice@example.com",
      "error": "processor tokens already in use: tok-xyz"
    }
  ]
}
```

{% endcode %}

If multiple tokens conflicted, all conflicting tokens are listed in the error message:

{% code title="JSON" %}

```json
{
  "success_count": 0,
  "succeeded": [],
  "failed": [
    {
      "email": "alice@example.com",
      "error": "processor tokens already in use: tok-xyz, tok-abc"
    }
  ]
}
```

{% endcode %}

***

### Example: Capacity exceeded

Alice already has 23 processor tokens. You try to add 5 more, which would bring her total to 28 (over the limit of 25).

**Response:**

{% code title="JSON" %}

```json
{
  "success_count": 0,
  "succeeded": [],
  "failed": [
    {
      "email": "alice@example.com",
      "error": "cannot exceed 25 processor tokens per email (current: 23, adding: 5)"
    }
  ]
}
```

{% endcode %}

***

## Important rules

### Emails

* **Emails are case-insensitive.** `Alice@Example.com` and `alice@example.com` are treated as the same email.
* **Whitespace is trimmed automatically.** Leading and trailing spaces on emails are stripped before processing.
* **No duplicate emails in the same request.** Each email can only appear once. If you need to add tokens from two separate lists to the same email, combine them into one item.
* **The email must already have an invite token.** If you haven't created one yet via `POST /v2/invite-tokens`, this endpoint won't work for that email.

### Processor tokens

* **Whitespace is trimmed automatically.** Leading and trailing spaces on processor tokens are stripped.
* **Tokens are deduped within each email.** If you accidentally send `["tok-1", "tok-1"]` for one email, the duplicate is silently removed.
* **No duplicate tokens across emails in the same request.** If `alice@example.com` and `bob@example.com` both include `"tok-xyz"`, the second email will fail. Each processor token can only appear under one email per request.
* **Processor tokens are globally unique.** A processor token can only be associated with one invite token in the entire system, across all organizations. If `"tok-xyz"` is already used by anyone (even in a different organization), it cannot be appended.
* **Tokens already on this invite are skipped, not rejected.** If you try to append `"tok-1"` to an email that already has `"tok-1"`, we treat it as a no-op — the email is reported as succeeded, and the full list of tokens (including `"tok-1"`) is returned.
* **Max 25 processor tokens per email, total.** This is the total count — existing tokens plus the new ones you're appending. If an email already has 20 tokens and you try to add 10 more, it will fail because 30 > 25.
* **Max 500 total processor tokens per request.** The total number of processor tokens across all items in a single request cannot exceed 500.
* **Per-email all-or-nothing.** If any processor token for an email conflicts (e.g. it's already used by someone else), the entire email is rejected — none of the tokens are added for that email. Fix or remove the conflicting token and retry the whole email.

***

## Error reference

### Per-item errors (inside `failed`)

These are errors for individual items. The request still returns HTTP 200 — the error is reported per item in the `failed` array. Other items in the same request may still succeed.

| Error message                                                         | What it means                                                                                  | What to do                                                                                             |
| --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `email is required`                                                   | The email field is missing or empty.                                                           | Provide a valid email address.                                                                         |
| `invalid email format`                                                | The email is not a valid email address.                                                        | Fix the email and retry.                                                                               |
| `email exceeds maximum length`                                        | The email is longer than 254 characters.                                                       | Use a shorter email address.                                                                           |
| `duplicate email in batch`                                            | The same email appears more than once in your request.                                         | Remove the duplicate and combine the tokens into one item.                                             |
| `at least one processor token is required`                            | The `processor_tokens` array is empty.                                                         | Add at least one processor token.                                                                      |
| `processor token cannot be empty`                                     | An empty string was provided as a processor token.                                             | Remove the empty string or provide a valid token.                                                      |
| `processor token exceeds maximum length`                              | A processor token is longer than 255 characters.                                               | Check that you're sending the right token.                                                             |
| `cannot provide more than 25 processor tokens per email`              | You sent more than 25 unique tokens for a single email (after duplicates are removed).         | Reduce the number of tokens for this email.                                                            |
| `processor token duplicated across items: <token>`                    | The same processor token appears under two different emails in your request.                   | Each token can only appear under one email per request.                                                |
| `no unused, non-expired invite token found for email`                 | This email doesn't have an invite token, or the invite token has already been used or expired. | Create an invite token first via `POST /v2/invite-tokens`, or create a new one if the old one expired. |
| `cannot exceed 25 processor tokens per email (current: N, adding: M)` | Adding these tokens would bring the total over 25.                                             | Remove some tokens, or remove some existing ones first.                                                |
| `processor tokens already in use: <token1>, <token2>`                 | One or more processor tokens are already used by another invite (possibly in a different org). | Generate new processor tokens from your financial institution.                                         |

### HTTP errors (the whole request fails)

These errors mean nothing was processed. The entire request is rejected.

| Status | When                                                                                                                                                                |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 401    | `x-partner` header is missing, invalid, or the organization was not found.                                                                                          |
| 400    | The request body is malformed, the `items` array is empty or exceeds 100, total processor tokens exceed 500, or the organization does not support processor tokens. |
| 500    | Something went wrong on our end. Safe to retry the request.                                                                                                         |

***

## Limits

| Limit                                  | Value          |
| -------------------------------------- | -------------- |
| Max items per request                  | 100            |
| Max processor tokens per email (total) | 25             |
| Max total processor tokens per request | 500            |
| Max email length                       | 254 characters |
| Max processor token length             | 255 characters |

<br>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://developers.bloomcredit.io/consumer-permissioned-data/bring-your-own-token/appending-processor-tokens.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
