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

# Connected Accounts

> Charge on behalf of other Recurrente accounts

If you're building a platform or marketplace, and you want to:

* Charge on behalf of someone else, or
* Share revenue with other accounts

Connected Accounts is for you.

With Connected Accounts you can **charge on behalf of other accounts using your own API keys**, without needing access to their API keys.

## Two ways to connect accounts

You can work with Connected Accounts in two ways:

1. Create and manage a new connected account from your platform. This is the recommended flow when your platform onboards the merchant, manages their data, and configures their bank account.
2. Connect an existing Recurrente account. This flow is useful when the merchant already uses Recurrente and only wants to authorize your platform to charge on their behalf.

Both paths end with a child account connected to your account. Use that child ID in `X-ACCOUNT-ID` to work with its resources. Endpoints that already accept `account_id`, such as checkouts and transfers, keep it as an alias.

## Work inside a connected account

Send your own platform key together with `X-ACCOUNT-ID`. The request is limited to resources belonging to that child, but it isn't filtered by who created them:

```bash
curl https://app.recurrente.com/api/checkouts \
  -H "X-SECRET-KEY: your_secret_key" \
  -H "X-ACCOUNT-ID: ac_123456"

curl https://app.recurrente.com/api/intents \
  -H "X-SECRET-KEY: your_secret_key" \
  -H "X-ACCOUNT-ID: ac_123456"
```

The platform can therefore retrieve checkouts and intents it created for the child as well as records the child created with its own keys. The child sees all of its own resources without sending the header. A platform can't select unrelated accounts or accounts connected only through a child.

## Supported account types

Both the platform and an existing recipient account can be personal or business accounts. Connect an existing personal account with the invitation link in **Settings → Connected accounts**: its administrator selects that account and confirms the connection, without sharing API keys or creating a business account.

The `account_type: individual` accepted by `POST /connected_accounts` means an individual **business** account, not a personal account. That endpoint's account-creation types are unchanged; use the invitation flow for existing personal accounts. Verification, processing limits, and money-movement restrictions still apply. Account-type support is separate from approval of the platform's use case.

## Create and connect an account by API

You can create a connected account managed by your platform with `POST /api/connected_accounts`. Recurrente creates the child account, connects it to your account, validates the required documents, and marks onboarding as completed.

If a connected account already exists for the same `email`, Recurrente reuses that account and updates the submitted data. `tax_id` and `tax_name` are extracted automatically from the tax registration document sent in `verification[tax_registration_document]`; you do not need to send them in the request.

## Required documents

Each account type requires different files in `verification`:

| Type         | Documents                                                                                                                                                            |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `individual` | `tax_registration_document`, `id_card_image`, `id_card_image_back`                                                                                                   |
| `business`   | `tax_registration_document`, `company_patent`, `commercial_patent`, `company_representative_document`, `company_representative_id`, `company_representative_id_back` |
| `non_profit` | `tax_registration_document`, `articles_of_incorporation`, `company_representative_document`, `company_representative_id`, `company_representative_id_back`           |

## Example: individual account

```bash
curl -X POST https://app.recurrente.com/api/connected_accounts \
  -H "X-SECRET-KEY: your_secret_key" \
  -F "email=ana@example.com" \
  -F "full_name=Ana Perez" \
  -F "phone_number=+50255555555" \
  -F "name=Ana Perez" \
  -F "account_type=individual" \
  -F "withdrawals_schedule=daily" \
  -F "verification[tax_registration_document]=@/path/ana-tax-registration.pdf" \
  -F "verification[id_card_image]=@/path/ana-id-front.jpg" \
  -F "verification[id_card_image_back]=@/path/ana-id-back.jpg"
```

## Example: business account

You can send `bank_account` in the same request to configure the external bank account that will receive withdrawals. Use `GET /api/banks` to see valid `bank_name` values.

The bank account goes through the same validation as the app form. Bank, currency, and account-type format rules run first, followed by a bank lookup through Nexa. A valid confirmed name replaces `holder_name`. If the bank reports the account as compromised, the request fails with `400` and `errors.number` without creating the connected account.

If Nexa is unavailable, the bank is unsupported, or the limit of 20 lookups per Recurrente account in 24 hours is reached, the supplied name is saved unconfirmed. When the bank cannot find the account, supplying `holder_name` also allows saving it unconfirmed, just like the form. Sandbox never queries the bank. Resubmitting an existing destination does not query it again.

When creating a new connected account, the lookup uses the parent account's budget. Its answer is kept for one hour even if creation fails, so retries can reuse it. An existing connected account uses its own budget.

**Banrural:** `checking` means monetaria and `savings` means ahorro. For GTQ, checking accounts accept prefixes `0` or `3` (10 or 14 digits), while savings accounts accept `0` or `4` (10, 13, or 14 digits). A number starting with `4` submitted as `checking` still fails format validation before Nexa is queried; submit the account's actual type. `GET /api/banks` lists banks, not their account-format rules.

```bash
curl -X POST https://app.recurrente.com/api/connected_accounts \
  -H "X-SECRET-KEY: your_secret_key" \
  -F "email=owner@restaurant.example" \
  -F "full_name=Ana Perez" \
  -F "phone_number=+50255555555" \
  -F "name=Acme Restaurant" \
  -F "account_type=business" \
  -F "withdrawals_schedule=daily" \
  -F "verification[tax_registration_document]=@/path/acme-tax-registration.pdf" \
  -F "verification[company_patent]=@/path/company-patent.pdf" \
  -F "verification[commercial_patent]=@/path/commercial-patent.pdf" \
  -F "verification[company_representative_document]=@/path/legal-representation.pdf" \
  -F "verification[company_representative_id]=@/path/representative-id-front.jpg" \
  -F "verification[company_representative_id_back]=@/path/representative-id-back.jpg" \
  -F "bank_account[holder_name]=Acme Restaurant, S.A." \
  -F "bank_account[number]=1234567890" \
  -F "bank_account[bank_name]=Banco Industrial" \
  -F "bank_account[currency]=GTQ" \
  -F "bank_account[account_type]=checking" \
  -F "bank_account[is_preferred]=true"
```

## Example: non-profit account

```bash
curl -X POST https://app.recurrente.com/api/connected_accounts \
  -H "X-SECRET-KEY: your_secret_key" \
  -F "email=admin@foundation.example" \
  -F "full_name=Luis Garcia" \
  -F "phone_number=+50255555556" \
  -F "name=Example Foundation" \
  -F "account_type=non_profit" \
  -F "withdrawals_schedule=weekly" \
  -F "verification[tax_registration_document]=@/path/foundation-tax-registration.pdf" \
  -F "verification[articles_of_incorporation]=@/path/articles-of-incorporation.pdf" \
  -F "verification[company_representative_document]=@/path/legal-representation.pdf" \
  -F "verification[company_representative_id]=@/path/representative-id-front.jpg" \
  -F "verification[company_representative_id_back]=@/path/representative-id-back.jpg"
```

## Update a connected account

You can update the public name and withdrawal frequency for a connected account:

```bash
curl -X PATCH https://app.recurrente.com/api/connected_accounts/ac_123456 \
  -H "X-SECRET-KEY: your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Restaurant Zona 10",
    "withdrawals_schedule": "weekly"
  }'
```

## Manage bank accounts

To add another external bank account to a connected account:

```bash
curl -X POST https://app.recurrente.com/api/connected_accounts/ac_123456/bank_accounts \
  -H "X-SECRET-KEY: your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "holder_name": "Acme Restaurant, S.A.",
    "number": "1234567890",
    "bank_name": "Banco Industrial",
    "currency": "GTQ",
    "account_type": "checking",
    "is_preferred": true
  }'
```

To change the default bank account, update the desired bank account with `is_preferred: true`:

```bash
curl -X PATCH https://app.recurrente.com/api/connected_accounts/ac_123456/bank_accounts/ba_123456 \
  -H "X-SECRET-KEY: your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{ "is_preferred": true }'
```

To remove a bank account, use `DELETE`. Recurrente archives it and stops showing it in the API.

## Connect an existing account

If the merchant already has a Recurrente account, you don't need to create another account by API. Connect both accounts from the Recurrente UI by following [these instructions](https://ayuda.recurrente.com/es/articles/9185861-cuentas-conectadas-como-cobrar-en-nombre-de-otra-cuenta).

## Create a checkout for a connected account

Once accounts are connected, create checkouts on behalf of the child with `X-ACCOUNT-ID`. `account_id` in the request body remains a compatibility alias:

```bash
curl -X POST https://app.recurrente.com/api/checkouts \
  -H "X-SECRET-KEY: your_secret_key" \
  -H "X-ACCOUNT-ID: ac_123456" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "currency": "GTQ",
        "amount_in_cents": 3000,
        "name": "Example product"
      }
    ]
  }'
```

| Parameter                                            | Description                                                  |
| ---------------------------------------------------- | ------------------------------------------------------------ |
| `X-ACCOUNT-ID`                                       | The connected child account ID that will receive the payment |
| `account_id`                                         | Compatibility alias for `X-ACCOUNT-ID` in the request body   |
| `application_fee_amount` / `application_fee_percent` | Optional. Commission the child account pays your platform    |
| `transfer_setups`                                    | Optional. Routes funds to other Recurrente accounts          |

## Platform commission (application fee)

To keep a commission on a connected account's sale, send `application_fee_amount` (one-time payments, in cents) or `application_fee_percent` (subscriptions, a percentage of each invoice's total, 0–100 with up to 2 decimal places) when creating the checkout on behalf of the child account:

```json
{
  "items": [
    {
      "currency": "GTQ",
      "amount_in_cents": 3000,
      "name": "Example product"
    }
  ],
  "account_id": "ac_123456",
  "application_fee_amount": 100
}
```

When the payment completes, Recurrente transfers the commission from the child account's balance to your platform's. For subscriptions, the commission is transferred on every successful charge while the subscription is active. The commission can be as high as the net available amount after fees, FEL, and VAT, can be reversed on refunds with `refund_application_fee`, and is included in daily commission invoicing when the connection has it enabled.

The checkout serializes the commission back as `application_fee_amount` / `application_fee_percent`, alongside the full detail in `transfer_setups` (the commission appears there with `purpose: "platform_commission"`).

> **Note**
>
> Before `application_fee_amount` / `application_fee_percent` existed, the commission was declared by hand as a `transfer_setups` element with `purpose: "platform_commission"`. That form is still accepted for compatibility and behaves identically, but it cannot be combined with the new parameters in the same checkout.

## Fund distribution

To route funds to other accounts (destination model: your account is the merchant of record and you send part of the net to a connected account), use `transfer_setups`. Each element defines a transfer that Recurrente executes automatically once the payment completes successfully:

* `recipient_id` is the ID of the **Recurrente** account that receives the funds (e.g. `ac_789012`) — not a bank account. Funds are credited to that account's Recurrente balance, and from there follow its normal withdrawal flow.
* For **one-time payments** send `amount_in_cents`: a fixed amount transferred once. It can be as high as the net available amount after fees, FEL, and VAT.
* For **subscriptions** send `amount_percent`: a percentage of each invoice's total (0–100, up to 2 decimal places), transferred on every successful charge while the subscription is active. At checkout creation we validate that the percentage fits within the net amount after fees, FEL, and VAT. If the subscription is cancelled, its recurring transfers are cancelled with it.

```json
{
  "items": [
    {
      "currency": "GTQ",
      "amount_in_cents": 3000,
      "name": "Example product"
    }
  ],
  "transfer_setups": [
    {
      "amount_in_cents": 2500,
      "recipient_id": "ac_789012"
    }
  ]
}
```

## Daily commission invoicing

You can ask Recurrente to issue one daily DTE per currency for the platform commissions (application fees) that each connected account pays to your platform. Your platform has a general setting (`daily` or `none`), and each connection can override it. It only covers completed live transfers after `enabled_at`; it does not classify or invoice historical transfers.

Enable it with a live API key. Your platform must be ready to issue FEL through INFILE, and the connected account must have a verified tax ID and legal name:

```bash
curl -X PUT https://app.recurrente.com/api/connected_accounts/ac_123456/commission_invoicing \
  -H "X-SECRET-KEY: your_live_key" \
  -H "Content-Type: application/json" \
  -d '{ "mode": "daily" }'
```

Recurrente processes closed `America/Guatemala` days asynchronously. Each document sums the exact completed commissions for one day and currency; it does not move the principal again. List documents and reconcile each line to its source transfer:

```bash
curl https://app.recurrente.com/api/connected_accounts/ac_123456/commission_invoices \
  -H "X-SECRET-KEY: your_live_key"

curl https://app.recurrente.com/api/connected_accounts/ac_123456/commission_invoices/pci_ab12cd34 \
  -H "X-SECRET-KEY: your_live_key"
```

The detail response exposes `transfer_setup_id`, `transfer_id`, `source_id`, `source_type`, and `completed_at`. The `partner_commission_invoice.issued` and `partner_commission_invoice.failed` webhooks signal the result; `failed` is only delivered when no further automatic retry is appropriate.

Sending `{ "mode": "none" }` sets an override that closes the interval. Sending `{ "mode": "default" }` removes the override and resumes inheriting the general setting. Recurrente still drains commissions completed before `disabled_at` and preserves every existing document and line. The issuing platform can continue reading that history after disconnecting the account; configuration still requires an active connection.

## The platform pays payment fees

The equivalent of Stripe's `defaults.responsibilities.fees_collector = application` is `fees_collector: "application"`. For a live checkout your platform creates on behalf of a connected account, Recurrente credits the connected account without deducting processor and Recurrente fees, and debits those fees from your platform balance at the same time. The connected account still pays its own withholding and FEL fee. This applies only to charges in GTQ or USD; in other currencies the connected account pays its fees as usual.

Configure one connection with a live API key. Your platform needs a verified tax ID and fiscal name:

```bash
curl -X PUT https://app.recurrente.com/api/connected_accounts/ac_123456/fee_collection \
  -H "X-SECRET-KEY: your_live_key" \
  -H "Content-Type: application/json" \
  -d '{ "fees_collector": "application" }'
```

Use `recurrente` for normal connected-account fee responsibility, or `default` to remove the override and inherit your platform's general setting. Responsibility is snapshotted on each paid charge; changing it never rewrites earlier payments.

Recurrente issues one daily DTE per currency to your platform for all fees it covered that day, even when they came from several connected accounts:

```bash
curl https://app.recurrente.com/api/connected_account_fee_invoices \
  -H "X-SECRET-KEY: your_live_key"

curl https://app.recurrente.com/api/connected_account_fee_invoices/cafi_ab12cd34 \
  -H "X-SECRET-KEY: your_live_key"
```

Detail lines include `connected_account_id`, `intent_id`, `payment_id`, `processor_fee_in_cents`, `recurrente_fee_in_cents`, and `paid_at`. The `connected_account_fee_invoice.issued` and `connected_account_fee_invoice.failed` webhooks report the fiscal result. Issuing the DTE never moves money again.

Processor and Recurrente fees are not returned to the platform when the charge is refunded. Their historical DTE remains unchanged.

## Refund a connected account charge

Your platform can refund a child account's payment intent with its own API key. Send `refund_application_fee: true` to also return that charge's platform commissions to the child account:

```bash
curl -X POST https://app.recurrente.com/api/refunds \
  -H "X-SECRET-KEY: your_secret_key" \
  -H "X-ACCOUNT-ID: ac_123456" \
  -H "Content-Type: application/json" \
  -d '{
    "payment_intent_id": "pa_ab12cd34",
    "refund_application_fee": true
  }'
```

The default is `false`. This parameter only affects the platform commission (transfers with `purpose: "platform_commission"`, including those created with `application_fee_amount` / `application_fee_percent`); recipients keep completed fund-split transfers. Both the child account and the platform can send it, but a platform API key must allow money movement.

Recurrente reserves the balance and reverses platform commissions before requesting the processor refund. The request fails before that call if the platform already spent the commission or if the child account can't cover the full refund. Pending one-time transfer setups are cancelled; for subscriptions, only the refunded invoice's fulfillment is affected and the template for future charges remains active.

If a commission already belongs to a daily DTE, Recurrente allows the refund when every line in that document belongs to the same payment and voids the DTE after the processor confirms the refund. If the document also includes commissions from other payments, the request fails instead of voiding their fiscal coverage. A DTE void error after a confirmed refund is reported for operational follow-up and doesn't undo the refund.

The response includes `refund_application_fee` and `transfer_adjustments`. Each reversal appears as a new transfer linked through `reversal_of_id` and `refund_id`; the original transfer is never edited or deleted. If the processor rejects the refund, Recurrente creates compensating movements and restores the transfer setups. A concurrent request receives `202` and doesn't call the processor twice.

If the processor outcome is indeterminate, the refund remains `pending` with `failure_reason: "provider_outcome_unknown"`. Recurrente keeps the reservation and movements, doesn't retry automatically, and a later POST returns `422` with the `refund_id`; retrieve that refund and contact support to reconcile it before attempting another operation. This first version only supports full refunds, and a successful refund that affected transfer setups can't later be voided.

## Webhooks

When a child account connects to your account, Recurrente sends an `account_connection.create` webhook to your account. The payload includes the connection and both accounts:

```json
{
  "id": "a_con_123456",
  "event_type": "account_connection.create",
  "status": "active",
  "api_version": "2024-04-24",
  "created_at": "2026-06-17T18:00:00Z",
  "live_mode": true,
  "parent_account": {
    "id": "ac_parent",
    "status": "active",
    "name": "Your platform",
    "account_type": "business",
    "created_at": "2026-06-17T17:00:00Z",
    "creator_name": "Ana Perez",
    "creator_email": "ana@example.com",
    "tax_id": "1234567",
    "tax_name": "Your Platform, S.A.",
    "withdrawals_schedule": "daily",
    "onboarding_completed": true
  },
  "child_account": {
    "id": "ac_child",
    "status": "active",
    "name": "Connected merchant",
    "account_type": "business",
    "created_at": "2026-06-17T18:00:00Z",
    "creator_name": "Juan Perez",
    "creator_email": "juan@example.com",
    "tax_id": "7654321",
    "tax_name": "Connected Merchant, S.A.",
    "withdrawals_schedule": "daily",
    "onboarding_completed": true
  }
}
```

When events occur in a child account, you'll receive webhooks with additional parameters:

* `connected: true` indicates the event was generated by a connected account
* `account_id: "ac_123456"` is the ID of the account that generated the event

You can use these fields to identify and process connected account events separately.