---
title: "Link Bank Account"
method: POST
path: "/extensions/roboledger/{graph_id}/operations/link-bank-account"
tags: ["RoboLedger: Setup"]
---

# Link Bank Account

`POST /extensions/roboledger/{graph_id}/operations/link-bank-account`

## Request

- Base URL: `https://api.robosystems.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.robosystems.ai/extensions/roboledger/{graph_id}/operations/link-bank-account`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

Point a bank feed's account at a chart account. Name `element_id` for an existing active account, or `entity_id` alone to create one in that entity's chart. The chart the account is in decides whose books the feed's lines go into, so this is also how a feed account is bound to a subsidiary. Lines still in the inbox move with it (across an entity change their suggestion is resolved again on the new chart, and a classification that named the old entity's account is dropped); posted entries stay where they were posted. An account another connection already feeds is refused, as is an entity whose books QuickBooks keeps (the group parent, while QuickBooks is connected). An account the feed created and then left stays on its chart as an ordinary account. A sync already in flight when the link moves can still land a line or two on the old account; running this again moves them. Read the group's accounts with the `bankAccounts` GraphQL field.

**Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours return the same envelope. Reusing the key with a different body returns HTTP 409 Conflict.

## Path parameters

- `graph_id` string, required

## Headers

- `Idempotency-Key` string, nullable

## Request body

- LinkBankAccountRequest: Point a bank feed's account at a chart account. Name `element_id` for an existing active account, or `entity_id` alone to create one in that entity's chart. The chart the account is in decides whose books the feed's lines go into, so this is also how a feed account is bound to a subsidiary. Lines still in the inbox move with it; posted entries stay where they were posted.
  - `connection_id` string, required: The feed's connection.
  - `account_id` string, required: The provider's id for the account.
  - `element_id` string, nullable: The chart account to link; its chart's entity takes the feed.
  - `entity_id` string, nullable: With `element_id`, the entity the account must belong to. Alone, the entity in whose chart a new account is created for the feed account.

## Response `200`

Successful Response

- OperationEnvelopeLinkBankAccountResponse
  - `operation` string, required: Kebab-case operation name
  - `operationId` string, required: op_-prefixed ULID for audit and SSE correlation
  - `status` 'completed' | 'pending' | 'failed', required: Operation lifecycle state
  - `result` LinkBankAccountResponse: What `link-bank-account` did.
    - `connection_id` string, required
    - `provider` string, required
    - `account_id` string, required
    - `element_id` string, required: The chart account the feed now books to.
    - `previous_element_id` string, required
    - `entity_id` string, required: The entity the feed's lines now belong to.
    - `account_created` boolean: A new account was created in the entity's chart.
    - `events_repointed` integer: Inbox lines moved to the new account (posted entries stay).
    - `events_unclassified` integer: Lines returned to `captured`: their classification named an account in the previous entity's chart.
    - `pairs_across_entities` integer: Open transfer pairs whose two legs now sit on two entities, because only one leg's account moved. Intercompany; the commit guard refuses them until the other leg follows.
    - `changed` boolean: False when the link already stood and no line moved.
  - `at` string, required: ISO-8601 UTC timestamp
  - `createdBy` string, nullable: User ID that initiated the operation
  - `idempotentReplay` boolean: True when this envelope came from the idempotency cache — the underlying command did not execute again. False on fresh executions.

## Other responses

- `400`: Invalid request
- `401`: Authentication required
- `403`: Access denied
- `404`: Resource not found
- `409`: Idempotency-Key conflict — key reused with different body
- `422`: Validation error
- `429`: Rate limit exceeded
- `500`: Internal server error

---

[API](https://skmtc.dev/robosystems/apis/robosystems-api.md) · [All operations](https://skmtc.dev/robosystems/apis/robosystems-api/llms.txt) · [OpenAPI document](https://skmtc.dev/robosystems/apis/robosystems-api/revisions/28ffd375c457?raw)
