# Processor connections tools

> MCP tools for Processor connections: processor_connections_create, processor_connections_disconnect, processor_connections_events_list, processor_connections_get, processor_connections_list, processor_connections_routing_get, processor_connections_routing_update, processor_connections_test, processor_connections_update.

- `processor_connections_create`: Connect a payment processor
- `processor_connections_disconnect`: Disconnect a processor connection
- `processor_connections_events_list`: List a connection's webhook events
- `processor_connections_get`: Get a processor connection
- `processor_connections_list`: List processor connections
- `processor_connections_routing_get`: Show which processor connection serves each brand
- `processor_connections_routing_update`: Set a brand's processor connections
- `processor_connections_test`: Test a processor connection
- `processor_connections_update`: Update a processor connection

## `processor_connections_create`

**Connect a payment processor.** Operation [`processor_connections.create`](https://ownrecord.io/docs/reference/processor-connections-create.md) · Legal entity level · permission `processors:admin` · risk tier `security` · since M4

Connects a payment processor account to the legal entity of the request, in the request's mode. Ownrecord
tests the key at the processor, stores it encrypted (it is never shown again), and sets up the webhook
endpoint the processor sends payment events to.

- **Stripe** (`processor: "stripe"`): a restricted key (`rk_live_…`; test mode also takes `rk_test_…` or
  `sk_test_…`) with write access to PaymentIntents, SetupIntents, PaymentMethods, Customers, Refunds, Disputes
  and Webhook Endpoints and read access to Balance and Payouts, plus the account's publishable key (`pk_…`).
  A key missing permissions is refused with `processor_permissions_missing` listing them. Ownrecord creates
  the webhook endpoint on the account itself; on a development machine (no public URL) pass the signing secret
  of `stripe listen --print-secret` as `webhookSecret` instead and forward events with the Stripe CLI.
- **Mollie** (`processor: "mollie"`): an API key of the request's mode (`live_…` or `test_…`, from Mollie's
  dashboard → Developers → API keys), bound to one website profile; or an organization access token
  (`access_…`) with `profileId` (`pfl_…`), which reconciliation of Mollie's balance transactions needs (an API key
  is accepted and reports `balances.read` as missing). Checkout redirects buyers to Mollie's payment page, which
  offers the methods enabled on the profile (cards, iDEAL, Bancontact, SEPA Direct Debit for renewals, …).
  Mollie needs no webhook setup: every payment Ownrecord creates carries this connection's webhook URL.
- **Fake** (`processor: "fake"`): the development and CI processor, only where development access is on. The
  key is optional.

Brands then use the connection for checkout (spec §15.4; `processor_connections.routing.update` sets a brand's
primary and secondary). Needs a sign-in within the last 15 minutes in the console; security tier for agents.

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: true`

### Arguments

| Argument          | Type                              | Required | Description                                                                                                                                                                                              |
| ----------------- | --------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `processor`       | one of `stripe`, `mollie`, `fake` | yes      | –                                                                                                                                                                                                        |
| `label`           | string                            | no       | (1–100 characters)                                                                                                                                                                                       |
| `apiKey`          | string                            | no       | (1–4096 characters)                                                                                                                                                                                      |
| `publishableKey`  | string                            | no       | –                                                                                                                                                                                                        |
| `webhookSecret`   | string                            | no       | (8–4096 characters)                                                                                                                                                                                      |
| `profileId`       | ID (`pfl_…`)                      | no       | –                                                                                                                                                                                                        |
| `entity_id`       | ID (`ent_…`)                      | no       | The legal entity to act on (ent\_…), inside your API key's reach. Needed with an organization key; entity and brand keys act on their own entity.                                                        |
| `idempotency_key` | string                            | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |

- **Legal entity:** pass `entity_id` with an organization key; entity and brand keys act on their own entity.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** not supported. `dry_run: true` fails with `dry_run_not_supported`.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A key is missing or isn't shaped like the processor's keys, or a field doesn't apply to the processor.
- [`processor_not_available`](https://ownrecord.io/docs/reference/errors.md#processor_not_available): The fake processor outside development.
- [`processor_key_mode_mismatch`](https://ownrecord.io/docs/reference/errors.md#processor_key_mode_mismatch): A key belongs to the other mode.
- [`processor_key_not_restricted`](https://ownrecord.io/docs/reference/errors.md#processor_key_not_restricted): A live connection with the account's secret key (s&#x6B;_&#x6C;ive_…).
- [`processor_credentials_invalid`](https://ownrecord.io/docs/reference/errors.md#processor_credentials_invalid): The processor rejected the key.
- [`processor_permissions_missing`](https://ownrecord.io/docs/reference/errors.md#processor_permissions_missing): The restricted key lacks permissions (`details.missing_permissions`).
- [`processor_webhook_url_not_public`](https://ownrecord.io/docs/reference/errors.md#processor_webhook_url_not_public): Stripe can't reach this deployment; pass `webhookSecret`.
- [`processor_account_already_connected`](https://ownrecord.io/docs/reference/errors.md#processor_account_already_connected): The account is already connected to the entity in this mode.
- [`processor_request_failed`](https://ownrecord.io/docs/reference/errors.md#processor_request_failed): The processor couldn't be reached or refused the request.
- [`upstream_timeout`](https://ownrecord.io/docs/reference/errors.md#upstream_timeout): The processor didn't answer in time.
- [`scope_required`](https://ownrecord.io/docs/reference/errors.md#scope_required): The request names no legal entity.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't manage processor connections here.
- [`fresh_auth_required`](https://ownrecord.io/docs/reference/errors.md#fresh_auth_required): Your last sign-in is older than 15 minutes.

### Examples

**Connect Stripe in test mode**

Arguments:

```json
{
  "processor": "stripe",
  "apiKey": "rk_test_ExampleOnlyNotARealKey000000000AbCd",
  "publishableKey": "pk_test_ExampleOnlyNotARealKey000000000AbCd",
  "entity_id": "ent_01jbx4m3v8e9t0s6k2c5n7q1wy",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
  "object": "processor_connection",
  "entityId": "ent_01m4ttwd3veh9apbewxheq5xh4",
  "livemode": false,
  "processor": "stripe",
  "label": "Stripe",
  "status": "active",
  "statusReason": null,
  "accountId": "acct_1ExampleAccount01",
  "keyHint": "rk_test_…AbCd",
  "publishableKey": "pk_test_ExampleOnlyNotARealKey000000000AbCd",
  "checkoutComponent": "stripe_payment_element",
  "webhook": {
    "mode": "managed",
    "url": "https://ownrecord.io/api/webhooks/stripe/pcon_01m4vyt4mxe0s9zzk90373z6p6",
    "endpointId": "we_1ExampleEndpoint01",
    "lastEventAt": null
  },
  "lastTest": {
    "ok": true,
    "missingPermissions": [],
    "message": null,
    "checkedAt": "2026-10-14T12:00:00.000Z"
  },
  "failures": null,
  "disconnectedAt": null,
  "createdAt": "2026-10-14T12:00:00.000Z",
  "updatedAt": "2026-10-14T12:00:00.000Z",
  "version": 1
}
```

**Connect Mollie in test mode with an API key**

Arguments:

```json
{
  "processor": "mollie",
  "apiKey": "test_ExampleOnlyNotARealMollieKey0AbCd",
  "entity_id": "ent_01jbx4m3v8e9t0s6k2c5n7q1wy",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "pcon_01m4vyt4mxe0s9zzk90373z6p9",
  "object": "processor_connection",
  "entityId": "ent_01m4ttwd3veh9apbewxheq5xh4",
  "livemode": false,
  "processor": "mollie",
  "label": "Mollie",
  "status": "active",
  "statusReason": null,
  "accountId": "pfl_ExampleProf1",
  "keyHint": "test_…AbCd",
  "publishableKey": null,
  "checkoutComponent": "redirect",
  "webhook": {
    "mode": "managed",
    "url": "https://ownrecord.io/api/webhooks/mollie/pcon_01m4vyt4mxe0s9zzk90373z6p9",
    "endpointId": "mollie_payment_webhooks_0123456789abcdef01234567",
    "lastEventAt": null
  },
  "lastTest": {
    "ok": true,
    "missingPermissions": ["balances.read"],
    "message": "Connected with an API key. Reconciliation of Mollie balance transactions needs an organization access token (access_…) with balances.read.",
    "checkedAt": "2026-10-14T12:00:00.000Z"
  },
  "failures": null,
  "disconnectedAt": null,
  "createdAt": "2026-10-14T12:00:00.000Z",
  "updatedAt": "2026-10-14T12:00:00.000Z",
  "version": 1
}
```

**Use the fake processor in development**

Arguments:

```json
{
  "processor": "fake",
  "entity_id": "ent_01jbx4m3v8e9t0s6k2c5n7q1wy",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "pcon_01m4vyt4mxe0s9zzk90373z6p7",
  "object": "processor_connection",
  "entityId": "ent_01m4ttwd3veh9apbewxheq5xh4",
  "livemode": false,
  "processor": "fake",
  "label": "Fake processor",
  "status": "active",
  "statusReason": null,
  "accountId": "acct_fake_0123456789abcdef",
  "keyHint": "fake_…9f2c",
  "publishableKey": null,
  "checkoutComponent": "fake",
  "webhook": {
    "mode": "managed",
    "url": "http://localhost:3000/api/webhooks/fake/pcon_01m4vyt4mxe0s9zzk90373z6p7",
    "endpointId": "we_fake_0123456789abcdef01234567",
    "lastEventAt": null
  },
  "lastTest": {
    "ok": true,
    "missingPermissions": [],
    "message": null,
    "checkedAt": "2026-10-14T12:00:00.000Z"
  },
  "failures": null,
  "disconnectedAt": null,
  "createdAt": "2026-10-14T12:00:00.000Z",
  "updatedAt": "2026-10-14T12:00:00.000Z",
  "version": 1
}
```

## `processor_connections_disconnect`

**Disconnect a processor connection.** Operation [`processor_connections.disconnect`](https://ownrecord.io/docs/reference/processor-connections-disconnect.md) · Legal entity level · permission `processors:admin` · risk tier `security` · since M4

Disconnects a processor connection: its stored API key is destroyed and it can't take or refund payments any
more. Brands that use it as their primary or secondary connection must be assigned another one first
(`processor_connection_in_use`). Webhook events the processor still sends for earlier payments are recorded.
Delete the webhook endpoint in the processor's dashboard if you don't want them. To use the account again,
connect it again with `processor_connections.create`.

Needs a sign-in within the last 15 minutes in the console; security tier for agents. Dry-run it to see the
result first.

Annotations: `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument          | Type          | Required | Description                                                                                                                                                                                              |
| ----------------- | ------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`              | ID (`pcon_…`) | yes      | –                                                                                                                                                                                                        |
| `entity_id`       | ID (`ent_…`)  | no       | The legal entity to act on (ent\_…), inside your API key's reach. Needed with an organization key; entity and brand keys act on their own entity.                                                        |
| `idempotency_key` | string        | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean       | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Legal entity:** pass `entity_id` with an organization key; entity and brand keys act on their own entity.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No such connection for this entity in this mode.
- [`processor_connection_in_use`](https://ownrecord.io/docs/reference/errors.md#processor_connection_in_use): A brand uses it as primary or secondary (`details.brand_ids`).
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The connection changed meanwhile; try again.
- [`scope_required`](https://ownrecord.io/docs/reference/errors.md#scope_required): The request names no legal entity.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't manage processor connections here.
- [`fresh_auth_required`](https://ownrecord.io/docs/reference/errors.md#fresh_auth_required): Your last sign-in is older than 15 minutes.

### Example

**Disconnect a connection no brand uses**

Arguments:

```json
{
  "id": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
  "entity_id": "ent_01jbx4m3v8e9t0s6k2c5n7q1wy",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
  "object": "processor_connection",
  "entityId": "ent_01m4ttwd3veh9apbewxheq5xh4",
  "livemode": false,
  "processor": "stripe",
  "label": "Stripe",
  "status": "disconnected",
  "statusReason": null,
  "accountId": "acct_1ExampleAccount01",
  "keyHint": "rk_test_…AbCd",
  "publishableKey": "pk_test_ExampleOnlyNotARealKey000000000AbCd",
  "checkoutComponent": "stripe_payment_element",
  "webhook": {
    "mode": "managed",
    "url": "https://ownrecord.io/api/webhooks/stripe/pcon_01m4vyt4mxe0s9zzk90373z6p6",
    "endpointId": "we_1ExampleEndpoint01",
    "lastEventAt": null
  },
  "lastTest": {
    "ok": true,
    "missingPermissions": [],
    "message": null,
    "checkedAt": "2026-10-14T12:00:00.000Z"
  },
  "failures": null,
  "disconnectedAt": "2026-10-14T12:05:00.000Z",
  "createdAt": "2026-10-14T12:00:00.000Z",
  "updatedAt": "2026-10-14T12:05:00.000Z",
  "version": 2
}
```

## `processor_connections_events_list`

**List a connection's webhook events.** Operation [`processor_connections.events.list`](https://ownrecord.io/docs/reference/processor-connections-events-list.md) · Legal entity level · permission `processors:read` · risk tier `read` · since M4

Lists the verified webhook events a processor connection received, newest first: the processor's event ID and
type, what Ownrecord made of it (`normalizedKind`), its status (`pending`, `processed`, `failed` with the
error) and the handlers that ran. Events with a bad signature are refused at the door and never listed. The
processor's payload isn't returned. Paginate with `limit` and `starting_after`.

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument         | Type          | Required | Description                                                                                                                                       |
| ---------------- | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`             | ID (`pcon_…`) | yes      | –                                                                                                                                                 |
| `limit`          | integer       | no       | (1–100)                                                                                                                                           |
| `starting_after` | ID (`pevt_…`) | no       | –                                                                                                                                                 |
| `entity_id`      | ID (`ent_…`)  | no       | The legal entity to act on (ent\_…), inside your API key's reach. Needed with an organization key; entity and brand keys act on their own entity. |

- **Legal entity:** pass `entity_id` with an organization key; entity and brand keys act on their own entity.
- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No such connection for this entity in this mode.
- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A pagination value is invalid.
- [`scope_required`](https://ownrecord.io/docs/reference/errors.md#scope_required): The request names no legal entity.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't see processor connections here.

### Example

**Recent events**

Arguments:

```json
{
  "id": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
  "limit": 20,
  "entity_id": "ent_01jbx4m3v8e9t0s6k2c5n7q1wy"
}
```

Result `output`:

```json
{
  "object": "list",
  "data": [
    {
      "id": "pevt_01m4vyt4mxe0s9zzk90373z6p8",
      "object": "processor_event",
      "connectionId": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
      "processor": "stripe",
      "externalEventId": "evt_3ExampleEvent0001",
      "type": "payment_intent.succeeded",
      "normalizedKind": "payment.succeeded",
      "status": "processed",
      "attempts": 1,
      "handlers": [],
      "error": null,
      "receivedAt": "2026-10-14T12:05:00.000Z",
      "processedAt": "2026-10-14T12:05:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

## `processor_connections_get`

**Get a processor connection.** Operation [`processor_connections.get`](https://ownrecord.io/docs/reference/processor-connections-get.md) · Legal entity level · permission `processors:read` · risk tier `read` · since M4

Returns one processor connection (`pcon_…`) of the entity: status, key hint, publishable key, account, webhook
endpoint and last event, and the last connection test. Keys and secrets are never returned. Connections of
another entity or mode are `resource_not_found`.

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument    | Type          | Required | Description                                                                                                                                       |
| ----------- | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`        | ID (`pcon_…`) | yes      | –                                                                                                                                                 |
| `entity_id` | ID (`ent_…`)  | no       | The legal entity to act on (ent\_…), inside your API key's reach. Needed with an organization key; entity and brand keys act on their own entity. |

- **Legal entity:** pass `entity_id` with an organization key; entity and brand keys act on their own entity.
- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No such connection for this entity in this mode.
- [`scope_required`](https://ownrecord.io/docs/reference/errors.md#scope_required): The request names no legal entity.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't see processor connections here.

### Example

**A Stripe connection**

Arguments:

```json
{
  "id": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
  "entity_id": "ent_01jbx4m3v8e9t0s6k2c5n7q1wy"
}
```

Result `output`:

```json
{
  "id": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
  "object": "processor_connection",
  "entityId": "ent_01m4ttwd3veh9apbewxheq5xh4",
  "livemode": false,
  "processor": "stripe",
  "label": "Stripe",
  "status": "active",
  "statusReason": null,
  "accountId": "acct_1ExampleAccount01",
  "keyHint": "rk_test_…AbCd",
  "publishableKey": "pk_test_ExampleOnlyNotARealKey000000000AbCd",
  "checkoutComponent": "stripe_payment_element",
  "webhook": {
    "mode": "managed",
    "url": "https://ownrecord.io/api/webhooks/stripe/pcon_01m4vyt4mxe0s9zzk90373z6p6",
    "endpointId": "we_1ExampleEndpoint01",
    "lastEventAt": null
  },
  "lastTest": {
    "ok": true,
    "missingPermissions": [],
    "message": null,
    "checkedAt": "2026-10-14T12:00:00.000Z"
  },
  "failures": null,
  "disconnectedAt": null,
  "createdAt": "2026-10-14T12:00:00.000Z",
  "updatedAt": "2026-10-14T12:00:00.000Z",
  "version": 1
}
```

## `processor_connections_list`

**List processor connections.** Operation [`processor_connections.list`](https://ownrecord.io/docs/reference/processor-connections-list.md) · Legal entity level · permission `processors:read` · risk tier `read` · since M4

Lists the legal entity's processor connections in the mode of the request, newest first: processor, label,
status, the key hint (never the key), the webhook endpoint and when the last event arrived, and the last
connection test. Filter by `processor` and `status`; paginate with `limit` (1–100, default 10) and
`starting_after`.

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument         | Type                                        | Required | Description                                                                                                                                       |
| ---------------- | ------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `limit`          | integer                                     | no       | (1–100)                                                                                                                                           |
| `starting_after` | ID (`pcon_…`)                               | no       | –                                                                                                                                                 |
| `processor`      | one of `stripe`, `mollie`, `fake`           | no       | –                                                                                                                                                 |
| `status`         | one of `active`, `degraded`, `disconnected` | no       | –                                                                                                                                                 |
| `entity_id`      | ID (`ent_…`)                                | no       | The legal entity to act on (ent\_…), inside your API key's reach. Needed with an organization key; entity and brand keys act on their own entity. |

- **Legal entity:** pass `entity_id` with an organization key; entity and brand keys act on their own entity.
- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A filter or pagination value is invalid.
- [`scope_required`](https://ownrecord.io/docs/reference/errors.md#scope_required): The request names no legal entity.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't see processor connections here.

### Example

**The entity's connections**

Arguments:

```json
{
  "entity_id": "ent_01jbx4m3v8e9t0s6k2c5n7q1wy"
}
```

Result `output`:

```json
{
  "object": "list",
  "data": [
    {
      "id": "pcon_01m4vyt4mxe0s9zzk90373z6p7",
      "object": "processor_connection",
      "entityId": "ent_01m4ttwd3veh9apbewxheq5xh4",
      "livemode": false,
      "processor": "fake",
      "label": "Fake processor",
      "status": "active",
      "statusReason": null,
      "accountId": "acct_fake_0123456789abcdef",
      "keyHint": "fake_…9f2c",
      "publishableKey": null,
      "checkoutComponent": "fake",
      "webhook": {
        "mode": "managed",
        "url": "http://localhost:3000/api/webhooks/fake/pcon_01m4vyt4mxe0s9zzk90373z6p7",
        "endpointId": "we_fake_0123456789abcdef01234567",
        "lastEventAt": null
      },
      "lastTest": {
        "ok": true,
        "missingPermissions": [],
        "message": null,
        "checkedAt": "2026-10-14T12:00:00.000Z"
      },
      "failures": null,
      "disconnectedAt": null,
      "createdAt": "2026-10-14T12:00:00.000Z",
      "updatedAt": "2026-10-14T12:00:00.000Z",
      "version": 1
    },
    {
      "id": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
      "object": "processor_connection",
      "entityId": "ent_01m4ttwd3veh9apbewxheq5xh4",
      "livemode": false,
      "processor": "stripe",
      "label": "Stripe",
      "status": "active",
      "statusReason": null,
      "accountId": "acct_1ExampleAccount01",
      "keyHint": "rk_test_…AbCd",
      "publishableKey": "pk_test_ExampleOnlyNotARealKey000000000AbCd",
      "checkoutComponent": "stripe_payment_element",
      "webhook": {
        "mode": "managed",
        "url": "https://ownrecord.io/api/webhooks/stripe/pcon_01m4vyt4mxe0s9zzk90373z6p6",
        "endpointId": "we_1ExampleEndpoint01",
        "lastEventAt": null
      },
      "lastTest": {
        "ok": true,
        "missingPermissions": [],
        "message": null,
        "checkedAt": "2026-10-14T12:00:00.000Z"
      },
      "failures": null,
      "disconnectedAt": null,
      "createdAt": "2026-10-14T12:00:00.000Z",
      "updatedAt": "2026-10-14T12:00:00.000Z",
      "version": 1
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

## `processor_connections_routing_get`

**Show processor routing.** Operation [`processor_connections.routing.get`](https://ownrecord.io/docs/reference/processor-connections-routing-get.md) · Legal entity level · permission `processors:read` · risk tier `read` · since M13

Lists the legal entity's brands with their processor routing in the request's mode (spec §15.4): the primary
and the secondary connection, and the connection that takes the brand's new checkouts now, with why
(`servingReason`): its primary; its secondary while the primary is degraded or disconnected; a degraded
connection when nothing better is available; or, when the brand names no connection of this mode, the entity's
only active one. Sessions that already started and subscriptions keep the connection they use.

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument    | Type         | Required | Description                                                                                                                                       |
| ----------- | ------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `entity_id` | ID (`ent_…`) | no       | The legal entity to act on (ent\_…), inside your API key's reach. Needed with an organization key; entity and brand keys act on their own entity. |

- **Legal entity:** pass `entity_id` with an organization key; entity and brand keys act on their own entity.
- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`scope_required`](https://ownrecord.io/docs/reference/errors.md#scope_required): The request names no legal entity.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't see processor connections here.

### Example

**A brand failed over to its secondary**

Arguments:

```json
{
  "entity_id": "ent_01jbx4m3v8e9t0s6k2c5n7q1wy"
}
```

Result `output`:

```json
{
  "object": "list",
  "data": [
    {
      "object": "processor_routing",
      "brandId": "brd_01m4ttwd3veh9apbewxhfbgr1a",
      "brandName": "Acme",
      "livemode": false,
      "primary": {
        "id": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
        "label": "Stripe",
        "processor": "stripe",
        "status": "degraded",
        "statusReason": "Marked degraded automatically after 5 failed requests in a row to the processor (last: api_error)."
      },
      "secondary": {
        "id": "pcon_01m4vyt4mxe0s9zzk90373z6p9",
        "label": "Mollie",
        "processor": "mollie",
        "status": "active",
        "statusReason": null
      },
      "serving": {
        "id": "pcon_01m4vyt4mxe0s9zzk90373z6p9",
        "label": "Mollie",
        "processor": "mollie",
        "status": "active",
        "statusReason": null
      },
      "servingReason": "secondary"
    }
  ]
}
```

## `processor_connections_routing_update`

**Set a brand's processor connections.** Operation [`processor_connections.routing.update`](https://ownrecord.io/docs/reference/processor-connections-routing-update.md) · Legal entity level · permission `processors:admin` · risk tier `write` · since M13

Sets which of the legal entity's processor connections a brand's checkouts use (spec §15.4): the `primary`,
and optionally a `secondary` that takes new checkouts while the primary is degraded (marked by an operator, or
automatically after repeated failed requests to the processor). Both must be connections of the entity in the
request's mode that aren't disconnected; several brands may share one. A secondary needs a plan with the
secondary processor feature (`processors.secondary`); `secondary: null` removes it. Sessions that already
started and subscriptions keep the connection they use (saved payment methods belong to their processor).

Pass `expectedVersion` (the brand's `version`) to refuse the change if the brand changed meanwhile.

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument          | Type                  | Required | Description                                                                                                                                                                                              |
| ----------------- | --------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `brandId`         | ID (`brd_…`)          | yes      | –                                                                                                                                                                                                        |
| `primary`         | ID (`pcon_…`)         | yes      | –                                                                                                                                                                                                        |
| `secondary`       | ID (`pcon_…`) or null | no       | –                                                                                                                                                                                                        |
| `expectedVersion` | integer               | no       | (1–9007199254740991)                                                                                                                                                                                     |
| `entity_id`       | ID (`ent_…`)          | no       | The legal entity to act on (ent\_…), inside your API key's reach. Needed with an organization key; entity and brand keys act on their own entity.                                                        |
| `idempotency_key` | string                | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean               | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Legal entity:** pass `entity_id` with an organization key; entity and brand keys act on their own entity.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No such brand of the entity, or no such connection of the entity in this mode.
- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): The primary and the secondary are the same connection.
- [`processor_connection_disconnected`](https://ownrecord.io/docs/reference/errors.md#processor_connection_disconnected): A connection is disconnected.
- [`entitlement_required`](https://ownrecord.io/docs/reference/errors.md#entitlement_required): A secondary connection on a plan without the secondary processor feature (`processors.secondary`).
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The brand changed since `expectedVersion`.
- [`scope_required`](https://ownrecord.io/docs/reference/errors.md#scope_required): The request names no legal entity.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't manage processor connections here.

### Example

**Stripe as primary, Mollie as failover**

Arguments:

```json
{
  "brandId": "brd_01m4ttwd3veh9apbewxhfbgr1a",
  "primary": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
  "secondary": "pcon_01m4vyt4mxe0s9zzk90373z6p9",
  "entity_id": "ent_01jbx4m3v8e9t0s6k2c5n7q1wy",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "object": "processor_routing",
  "brandId": "brd_01m4ttwd3veh9apbewxhfbgr1a",
  "brandName": "Acme",
  "livemode": false,
  "primary": {
    "id": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
    "label": "Stripe",
    "processor": "stripe",
    "status": "active",
    "statusReason": null
  },
  "secondary": {
    "id": "pcon_01m4vyt4mxe0s9zzk90373z6p9",
    "label": "Mollie",
    "processor": "mollie",
    "status": "active",
    "statusReason": null
  },
  "serving": {
    "id": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
    "label": "Stripe",
    "processor": "stripe",
    "status": "active",
    "statusReason": null
  },
  "servingReason": "primary"
}
```

## `processor_connections_test`

**Test a processor connection.** Operation [`processor_connections.test`](https://ownrecord.io/docs/reference/processor-connections-test.md) · Legal entity level · permission `processors:write` · risk tier `write` · since M4

Tests a connection's stored key at the processor: is it still accepted, for which account and mode, and which
permissions a restricted key lacks. The result is stored on the connection (`lastTest`) and returned; a failed
test isn't an error. The connection's status doesn't change (mark it `degraded` with
`processor_connections.update` to send new checkouts to the brand's secondary connection).

A dry run returns a simulated test without calling the processor or storing anything.

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: true`

### Arguments

| Argument          | Type          | Required | Description                                                                                                                                                                                              |
| ----------------- | ------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`              | ID (`pcon_…`) | yes      | –                                                                                                                                                                                                        |
| `entity_id`       | ID (`ent_…`)  | no       | The legal entity to act on (ent\_…), inside your API key's reach. Needed with an organization key; entity and brand keys act on their own entity.                                                        |
| `idempotency_key` | string        | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean       | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Legal entity:** pass `entity_id` with an organization key; entity and brand keys act on their own entity.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No such connection for this entity in this mode.
- [`processor_connection_disconnected`](https://ownrecord.io/docs/reference/errors.md#processor_connection_disconnected): The connection is disconnected (its key is gone).
- [`processor_not_available`](https://ownrecord.io/docs/reference/errors.md#processor_not_available): A fake connection where development access is off.
- [`processor_request_failed`](https://ownrecord.io/docs/reference/errors.md#processor_request_failed): The processor couldn't be reached.
- [`upstream_timeout`](https://ownrecord.io/docs/reference/errors.md#upstream_timeout): The processor didn't answer in time.
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The connection changed while it was being tested; test again.
- [`scope_required`](https://ownrecord.io/docs/reference/errors.md#scope_required): The request names no legal entity.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't test processor connections here.

### Example

**A key that lost a permission**

Arguments:

```json
{
  "id": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
  "entity_id": "ent_01jbx4m3v8e9t0s6k2c5n7q1wy",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
  "object": "processor_connection",
  "entityId": "ent_01m4ttwd3veh9apbewxheq5xh4",
  "livemode": false,
  "processor": "stripe",
  "label": "Stripe",
  "status": "active",
  "statusReason": null,
  "accountId": "acct_1ExampleAccount01",
  "keyHint": "rk_test_…AbCd",
  "publishableKey": "pk_test_ExampleOnlyNotARealKey000000000AbCd",
  "checkoutComponent": "stripe_payment_element",
  "webhook": {
    "mode": "managed",
    "url": "https://ownrecord.io/api/webhooks/stripe/pcon_01m4vyt4mxe0s9zzk90373z6p6",
    "endpointId": "we_1ExampleEndpoint01",
    "lastEventAt": null
  },
  "lastTest": {
    "ok": false,
    "missingPermissions": ["refunds:write"],
    "message": "The key is missing a permission.",
    "checkedAt": "2026-10-14T12:05:00.000Z"
  },
  "failures": null,
  "disconnectedAt": null,
  "createdAt": "2026-10-14T12:00:00.000Z",
  "updatedAt": "2026-10-14T12:05:00.000Z",
  "version": 2
}
```

## `processor_connections_update`

**Update a processor connection.** Operation [`processor_connections.update`](https://ownrecord.io/docs/reference/processor-connections-update.md) · Legal entity level · permission `processors:admin` · risk tier `security` · since M4

Changes a processor connection of the entity:

- `apiKey`: rotate the key. The new key is tested first and must belong to the same account; it replaces the
  stored one (encrypted, never shown again). `publishableKey` changes the publishable key.
- `webhookSecret`: store a new webhook signing secret (after rolling it in the processor's dashboard, or a new
  `stripe listen` secret). `repairWebhook: true` re-creates or re-enables the endpoint Ownrecord manages.
- `status`: `degraded` sends new checkout sessions to the brand's secondary connection (spec §15.4); `active`
  undoes it, clears the count of failed requests and resolves the `PROCESSOR_DEGRADED` alert (a connection that
  failed too many requests in a row is marked degraded automatically and only an operator marks it active
  again). `statusReason` says why. `label` renames it.
- Mollie: `apiKey` rotates the API key or organization access token (tested against the connection's profile);
  `webhookSecret` doesn't apply.

Pass `expectedVersion` (the `version` you read) to refuse the change if someone else changed the connection
meanwhile. Needs a sign-in within the last 15 minutes in the console; security tier for agents.

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: true`

### Arguments

| Argument          | Type                        | Required | Description                                                                                                                                                                                              |
| ----------------- | --------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`              | ID (`pcon_…`)               | yes      | –                                                                                                                                                                                                        |
| `label`           | string                      | no       | (1–100 characters)                                                                                                                                                                                       |
| `apiKey`          | string                      | no       | (1–4096 characters)                                                                                                                                                                                      |
| `publishableKey`  | string                      | no       | –                                                                                                                                                                                                        |
| `webhookSecret`   | string                      | no       | (8–4096 characters)                                                                                                                                                                                      |
| `repairWebhook`   | boolean                     | no       | –                                                                                                                                                                                                        |
| `status`          | one of `active`, `degraded` | no       | –                                                                                                                                                                                                        |
| `statusReason`    | string or null              | no       | –                                                                                                                                                                                                        |
| `expectedVersion` | integer                     | no       | (1–9007199254740991)                                                                                                                                                                                     |
| `entity_id`       | ID (`ent_…`)                | no       | The legal entity to act on (ent\_…), inside your API key's reach. Needed with an organization key; entity and brand keys act on their own entity.                                                        |
| `idempotency_key` | string                      | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |

- **Legal entity:** pass `entity_id` with an organization key; entity and brand keys act on their own entity.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** not supported. `dry_run: true` fails with `dry_run_not_supported`.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No such connection for this entity in this mode.
- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A field is invalid, or `repairWebhook` on a manual webhook.
- [`processor_connection_disconnected`](https://ownrecord.io/docs/reference/errors.md#processor_connection_disconnected): The connection is disconnected; connect again instead.
- [`processor_key_mode_mismatch`](https://ownrecord.io/docs/reference/errors.md#processor_key_mode_mismatch): A new key belongs to the other mode.
- [`processor_key_not_restricted`](https://ownrecord.io/docs/reference/errors.md#processor_key_not_restricted): A live connection with the account's secret key (s&#x6B;_&#x6C;ive_…).
- [`processor_credentials_invalid`](https://ownrecord.io/docs/reference/errors.md#processor_credentials_invalid): The processor rejected the new key, or it belongs to another account.
- [`processor_permissions_missing`](https://ownrecord.io/docs/reference/errors.md#processor_permissions_missing): The new key lacks permissions (`details.missing_permissions`).
- [`processor_webhook_url_not_public`](https://ownrecord.io/docs/reference/errors.md#processor_webhook_url_not_public): Stripe can't reach this deployment to re-create the endpoint.
- [`processor_not_available`](https://ownrecord.io/docs/reference/errors.md#processor_not_available): A fake connection where development access is off.
- [`processor_request_failed`](https://ownrecord.io/docs/reference/errors.md#processor_request_failed): The processor couldn't be reached or refused the request.
- [`upstream_timeout`](https://ownrecord.io/docs/reference/errors.md#upstream_timeout): The processor didn't answer in time.
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The connection changed since `expectedVersion`.
- [`scope_required`](https://ownrecord.io/docs/reference/errors.md#scope_required): The request names no legal entity.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't manage processor connections here.
- [`fresh_auth_required`](https://ownrecord.io/docs/reference/errors.md#fresh_auth_required): Your last sign-in is older than 15 minutes.

### Examples

**Rotate the restricted key**

Arguments:

```json
{
  "id": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
  "apiKey": "rk_test_ExampleOnlyNotARealKey000000000WxYz",
  "expectedVersion": 1,
  "entity_id": "ent_01jbx4m3v8e9t0s6k2c5n7q1wy",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
  "object": "processor_connection",
  "entityId": "ent_01m4ttwd3veh9apbewxheq5xh4",
  "livemode": false,
  "processor": "stripe",
  "label": "Stripe",
  "status": "active",
  "statusReason": null,
  "accountId": "acct_1ExampleAccount01",
  "keyHint": "rk_test_…WxYz",
  "publishableKey": "pk_test_ExampleOnlyNotARealKey000000000AbCd",
  "checkoutComponent": "stripe_payment_element",
  "webhook": {
    "mode": "managed",
    "url": "https://ownrecord.io/api/webhooks/stripe/pcon_01m4vyt4mxe0s9zzk90373z6p6",
    "endpointId": "we_1ExampleEndpoint01",
    "lastEventAt": null
  },
  "lastTest": {
    "ok": true,
    "missingPermissions": [],
    "message": null,
    "checkedAt": "2026-10-14T12:05:00.000Z"
  },
  "failures": null,
  "disconnectedAt": null,
  "createdAt": "2026-10-14T12:00:00.000Z",
  "updatedAt": "2026-10-14T12:05:00.000Z",
  "version": 2
}
```

**Mark it degraded**

Arguments:

```json
{
  "id": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
  "status": "degraded",
  "statusReason": "Stripe incident: card payments failing",
  "entity_id": "ent_01jbx4m3v8e9t0s6k2c5n7q1wy",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
  "object": "processor_connection",
  "entityId": "ent_01m4ttwd3veh9apbewxheq5xh4",
  "livemode": false,
  "processor": "stripe",
  "label": "Stripe",
  "status": "degraded",
  "statusReason": "Stripe incident: card payments failing",
  "accountId": "acct_1ExampleAccount01",
  "keyHint": "rk_test_…AbCd",
  "publishableKey": "pk_test_ExampleOnlyNotARealKey000000000AbCd",
  "checkoutComponent": "stripe_payment_element",
  "webhook": {
    "mode": "managed",
    "url": "https://ownrecord.io/api/webhooks/stripe/pcon_01m4vyt4mxe0s9zzk90373z6p6",
    "endpointId": "we_1ExampleEndpoint01",
    "lastEventAt": null
  },
  "lastTest": {
    "ok": true,
    "missingPermissions": [],
    "message": null,
    "checkedAt": "2026-10-14T12:00:00.000Z"
  },
  "failures": null,
  "disconnectedAt": null,
  "createdAt": "2026-10-14T12:00:00.000Z",
  "updatedAt": "2026-10-14T12:05:00.000Z",
  "version": 2
}
```
