---
title: "Begin a Ponto connection for one account"
method: POST
path: "/api/v1/banking/ponto/connect"
tags: ["Money"]
---

# Begin a Ponto connection for one account

`POST /api/v1/banking/ponto/connect`

## Request

- Base URL: `http://localhost:5180`
- URL: `http://localhost:5180/api/v1/banking/ponto/connect`
- Auth: API key in cookie `better-auth.session_token`

Mints the state and the PKCE challenge, writes them where the redirect will find them, and returns the URL to send the owner to. Connecting the same account again within half an hour hands back that same attempt, state and URL, so a return from either device connects it once. The first time a person connects from a team, Ponto is sent their name and email, and the company only where the owner filled in the team’s own details, so its sign-up opens filled in. If the owner picked a bank, its Ponto financial-institution id is included so that bank opens selected; a returning Ponto user is sent only their required email and that id. An address on a reserved domain is never sent. While bank accounts are charged, connecting an account this team does not read yet takes an owner on screen and a plan in trial or active, and one past the accounts the plan includes also takes `agreed` at the team’s current price, which the return links that one account at; connecting again one already read asks for nothing. Nothing is connected by calling this. Owners only.

## Request body

- object
  - `bankAccountId` string: The account in the portfolio the owner is connecting. Left out when they picked a bank from the list and the account will be made from what that bank shares.
  - `bankId` string: The bank they picked, from `/banking/banks`. Its Ponto financial-institution id is sent in the onboarding details so Ponto opens with that bank selected.
  - `agreed` object: The price a month of an account past the plan’s own that the owner saw and agreed to, copied from `billing.price` in GET /ponto/connections. Needed only where the request takes the team past the accounts its plan includes; without it, or at a price that is no longer the team’s, the answer is 409 with `details.reason` `price` and the current price.
    - `priceId` string, required: The Stripe price, as `billing.price.priceId` in GET /ponto/connections.
    - `unitAmount` integer, required: Minor units a month for one account past the plan’s own, as shown.
    - `currency` string, required: Lowercase, as shown.

## Response `200`

Where to send the owner.

- object
  - `ok` unknown, required
  - `data` object, required
    - `connectionId` string, required: Stable record id, e.g. `lse_012`.
    - `authorizeUrl` string, required: Send the owner here. Ponto signs them in or up, their bank asks them, and we never see a credential.
    - `state` string, required: Comes back on the redirect. Hand it to `/ponto/callback` exactly as it arrives.
  - `meta` object, required
    - `requestId` string, required: Echoed in the x-request-id header. Quote it in a bug report.

## Other responses

- `400`: The account belongs to the worked example, so a real bank account is never paired with it.
- `403`: Only an owner of the team may do this, and support may not do it for a customer; a member is refused with `details.reason` `owner_only`. While bank accounts are charged, adding a charged account also takes a person on screen, so an assistant’s token is refused with `details.reason` `on_screen`.
- `404`: No such bank account on this team.
- `409`: This deployment holds no Ponto credentials, so there is no connection to begin. Where the request adds an account: `details.reason` `needs_plan`, with `why` `no_plan` or `past_due` and the `price`, when the team has no plan in trial or active; where the feed is free, `why` `no_plan_unbilled` with no price, when a plan can be sold and the team has none; `price`, with the current `price`, when the account goes past the ones the plan includes and `agreed` is missing or no longer the team’s price. Whatever it adds, `unavailable` with `why` when the price cannot be read or charging is not set up on this deployment. Nothing is written in any of them.

---

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