Item Lifecycle

Overview

A Plaid item represents a user’s connection to a financial institution. It holds the Plaid access_token and item_id that are used for all subsequent data retrieval (transactions, accounts, balances, liabilities). A user can have multiple items, but typically only one is active at a time.

Items are created when a user links their bank account via Plaid Link. They transition through a defined set of statuses and are removed when the user disconnects their bank or cancels their account.

During reactivation, the item will have a TTL set when first created. The TTL is later if the user successfully completes reactivation. This will allow the item to be cleaned up if the user never completes the reactivation process.

See Plaid: Items for the full DynamoDB schema.

Item Statuses

Status Error Code Description

ACTIVE

(empty)

Item is connected and healthy. Transactions and accounts are being mined successfully.

ERROR

ITEM_LOGIN_REQUIRED

The user’s bank credentials have expired or changed. The user must re-authenticate via Plaid Link update mode.

ERROR

NO_ACCOUNTS

No accessible accounts were found on the item.

REMOVED

(any)

Item has been disconnected. The access token has been revoked at Plaid. No further mining occurs.

Status Transitions

              ┌─────────────────────────────────┐
              │                                 │
              ▼                                 │
           [ACTIVE] ──── item error ───► [ERROR]
              │                                 │
              │                         user re-authenticates
              │                         (Plaid Link update mode)
              │                                 │
              │           ◄─────────────────────┘
              │
              ├──── user removes ──────────────────► [REMOVED]
              │
              └──── force remove (admin) ──────────► [REMOVED]

Add Item Flow

Adding a new item is the primary onboarding step. The mobile app calls Plaid Link, receives a public_token, and submits it to the API. Version 2 (POST /{user_id}/transactions/items-v2) is the current preferred endpoint.

POST /{user_id}/transactions/items-v2
  { account_id, public_token, institution_id,
    is_reactivating, skip_activation }
        │
        ▼
Exchange public_token → access_token + item_id (Plaid)
        │
        ▼
Set webhook URL on item (Plaid)
        │  Kicks off INITIAL_UPDATE webhook → mining pipeline
        ▼
If is_reactivating flag is set to true, set a TTL on the item.
        │
        ▼
Save item record in DynamoDB
  status: ACTIVE
  main_account_id: provided account_id
        │
        ▼
If existing items have a different main_account_id:
  Update all existing items to use the new main_account_id
  (maintains consistency across multiple items)
        │
        ▼
Encrypt account + routing numbers (Payments Service)
        │
        ▼
skip_activation = false?
  └─► Activate user in User Service
        (sets user status → ACTIVE, activates subscription + membership)
        │
        ▼
Emit item-created event to EventBridge
        │
        ▼
Return 201 Created
AddItem v1 (POST /{user_id}/transactions/items) is deprecated. It has the same core flow but lacks the multi-item main account consistency logic introduced in v2.

Update Item Flow

Updating an item changes the main_account_id — the account used for float disbursements and subscription collection.

PATCH /{user_id}/transactions/items-v2/{item_id}
  { main_account_id }
        │
        ▼
User has an active float?
  AND existing items already have main_account_ids set?
        │ Yes
        └─► Return 412 Precondition Failed
            (cannot change main account while float is outstanding)
        │ No
        ▼
Update item: main_account_id = new value
        │
        ▼
If new main_account_id differs from other items:
  Update all other items to use the new main_account_id
        │
        ▼
Return 202 Accepted
UpdateItem v1 (PATCH /{user_id}/transactions/items/{item_id}) is deprecated. It lacks the active float guard and multi-item consistency logic.

Remove Item Flow

Two removal paths exist. Version 2 is preferred for all current use cases.

Remove v2 (Preferred)

DELETE /{user_id}/transactions/items-v2/{item_id}
        │
        ▼
Item has an active float?
        │ Yes
        └─► Return 412 Precondition Failed
        │ No
        ▼
Revoke access token at Plaid (RemoveItem)
        │ Plaid API failure → 503 Service Unavailable
        ▼
Mark item status → REMOVED in DynamoDB
        │
        ▼
Return 202 Accepted

Remove v1 (Deprecated)

Same core flow as v2, but also runs legacy employment-detection validation that can return 403 Forbidden if the user joined recently or has employment income detected. This validation was removed in v2.

Force Remove (Admin)

POST /{user_id}/transactions/items/{item_id}/remove/force
        │
        ▼
Revoke access token at Plaid (RemoveItem)
        │ Plaid API failure → 503 Service Unavailable
        ▼
Mark item status → REMOVED in DynamoDB
        │
        ▼
Return 202 Accepted

Force remove bypasses all safety checks including active float validation. It is restricted to admin callers.

Change Item Check

Before the mobile app initiates a bank change via Plaid Link, it calls CheckCanChangeItem to determine whether the change is permitted.

POST /{user_id}/transactions/items/{item_id}/change/check
        │
        ▼
Is this the main item AND user has an active float?
        │ Yes
        └─► Return 412 with ActiveFloatError
        │ No
        ▼
Return 200 with CheckCanChangeItem:
  { can_be_changed, date_of_last_change,
    days_until_next_change, total_wait_time }

The response includes cooldown information: in production, a 7-day cooldown (days_between_plaid_item_changes) applies after the last item change.

Remine

Remine queues an existing item for a fresh transaction and account fetch without waiting for a Plaid webhook.

POST /{user_id}/transactions/items/{item_id}/remine
        │
        ▼
Look up item in DynamoDB
        │ Not found → 404
        ▼
Send synthetic DEFAULT_UPDATE webhook to miner SQS
        │
        ▼
Return 202 Accepted

Mining then proceeds through the normal Plaid Mining Pipeline.

Persist Item

An item with a TTL set will be automatically cleaned up by DynamoDB. The persist endpoint removes the TTL to prevent this.

PATCH /{user_id}/transactions/items/{item_id}/persist
        │
        ▼
Look up item in DynamoDB
        │ Not found → 404
        ▼
Remove TTL attribute from item record
        │
        ▼
Return 202 Accepted

A link token is required to initialise a Plaid Link session in the mobile app. Two endpoints exist:

  • POST /{user_id}/transactions/link — Gets a link token for a new item connection. For legacy app versions, accepts a change_bank flag that enables update mode using the existing access token.

  • GET /{user_id}/transactions/items/{item_id}/link — Gets a link token in update mode for a specific existing item (to re-authenticate or add accounts).