# Update a processor connection

> The processor_connections.update operation: PATCH /api/v1/processor_connections/{id} in the REST API and the processor_connections_update tool in the MCP server.

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.

## At a glance

- **REST:** `PATCH /api/v1/processor_connections/{id}`
- **MCP tool:** `processor_connections_update`
- **Operation ID:** `processor_connections.update`
- **Scope:** Legal entity
- **Permission:** `processors:admin`
- **Risk tier:** `security`
- **Dry run:** Not supported
- **Idempotency:** `Idempotency-Key` optional
- **Modes:** Test and live
- **Authentication:** API key with the `processors:admin` scope
- **Fresh sign-in:** In the console, a sign-in within the last 15 minutes
- **Since:** M4

## Request

### Path parameters

| Field | Type        | Description |
| ----- | ----------- | ----------- |
| `id`  | `pcon_…` ID |             |

### Body

A JSON object (`Content-Type: application/json`):

| Field             | Type                        | Required | Description |
| ----------------- | --------------------------- | -------- | ----------- |
| `label`           | string                      | No       |             |
| `apiKey`          | string                      | No       |             |
| `publishableKey`  | string                      | No       |             |
| `webhookSecret`   | string                      | No       |             |
| `repairWebhook`   | boolean                     | No       |             |
| `status`          | one of `active`, `degraded` | No       |             |
| `statusReason`    | string or null              | No       |             |
| `expectedVersion` | integer (at least 1)        | No       |             |

## Response

`200 OK` with a JSON object:

| Field                         | Type                                                | Description |
| ----------------------------- | --------------------------------------------------- | ----------- |
| `id`                          | `pcon_…` ID                                         |             |
| `object`                      | `"processor_connection"`                            |             |
| `entityId`                    | `ent_…` ID                                          |             |
| `livemode`                    | boolean                                             |             |
| `processor`                   | one of `stripe`, `mollie`, `fake`                   |             |
| `label`                       | string                                              |             |
| `status`                      | one of `active`, `degraded`, `disconnected`         |             |
| `statusReason`                | string or null                                      |             |
| `accountId`                   | string or null                                      |             |
| `keyHint`                     | string                                              |             |
| `publishableKey`              | string or null                                      |             |
| `checkoutComponent`           | one of `stripe_payment_element`, `redirect`, `fake` |             |
| `webhook`                     | object                                              |             |
| `webhook.mode`                | one of `managed`, `manual`                          |             |
| `webhook.url`                 | string                                              |             |
| `webhook.endpointId`          | string or null                                      |             |
| `webhook.lastEventAt`         | date-time or null                                   |             |
| `lastTest`                    | object                                              |             |
| `lastTest.ok`                 | boolean                                             |             |
| `lastTest.missingPermissions` | array of string                                     |             |
| `lastTest.message`            | string or null                                      |             |
| `lastTest.checkedAt`          | date-time                                           |             |
| `failures`                    | object or null                                      |             |
| `disconnectedAt`              | date-time or null                                   |             |
| `createdAt`                   | date-time                                           |             |
| `updatedAt`                   | date-time                                           |             |
| `version`                     | integer (at least 1)                                |             |

## Examples

### Rotate the restricted key

```http
PATCH /api/v1/processor_connections/pcon_01m4vyt4mxe0s9zzk90373z6p6
Authorization: Bearer or_test_…
Content-Type: application/json

{
  "apiKey": "rk_test_ExampleOnlyNotARealKey000000000WxYz",
  "expectedVersion": 1
}
```

Response (`200`):

```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

```http
PATCH /api/v1/processor_connections/pcon_01m4vyt4mxe0s9zzk90373z6p6
Authorization: Bearer or_test_…
Content-Type: application/json

{
  "status": "degraded",
  "statusReason": "Stripe incident: card payments failing"
}
```

Response (`200`):

```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
}
```

## Errors

| Code                                                                                                                   | HTTP status | When                                                                   | What to do                                                                                                                                                                                                 |
| ---------------------------------------------------------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found)                               | 404         | No such connection for this entity in this mode.                       | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.                                                                                                       |
| [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed)                                 | 400         | A field is invalid, or `repairWebhook` on a manual webhook.            | Fix the fields listed in `details.issues` and send the request again.                                                                                                                                      |
| [`processor_connection_disconnected`](https://ownrecord.io/docs/reference/errors.md#processor_connection_disconnected) | 409         | The connection is disconnected; connect again instead.                 | Connect the processor again, or use another connection of the entity.                                                                                                                                      |
| [`processor_key_mode_mismatch`](https://ownrecord.io/docs/reference/errors.md#processor_key_mode_mismatch)             | 400         | A new key belongs to the other mode.                                   | Use a test key (Stripe r&#x6B;_&#x74;est_…, Mollie test\*…) for a test-mode connection and a live key (rk_live\*…, live\_…) for a live-mode connection, or switch the console's mode.                      |
| [`processor_key_not_restricted`](https://ownrecord.io/docs/reference/errors.md#processor_key_not_restricted)           | 400         | A live connection with the account's secret key (s&#x6B;_&#x6C;ive_…). | Create a restricted key (r&#x6B;_&#x6C;ive_…) with the permissions listed in the docs instead of using the account's secret key (s&#x6B;_&#x6C;ive_…).                                                     |
| [`processor_credentials_invalid`](https://ownrecord.io/docs/reference/errors.md#processor_credentials_invalid)         | 400         | The processor rejected the new key, or it belongs to another account.  | Copy the key again from the processor's dashboard (Stripe: Developers → API keys). It may have been rolled or deleted.                                                                                     |
| [`processor_permissions_missing`](https://ownrecord.io/docs/reference/errors.md#processor_permissions_missing)         | 400         | The new key lacks permissions (`details.missing_permissions`).         | Edit the restricted key in the processor's dashboard and grant the permissions listed in `details.missing_permissions`, then try again.                                                                    |
| [`processor_webhook_url_not_public`](https://ownrecord.io/docs/reference/errors.md#processor_webhook_url_not_public)   | 400         | Stripe can't reach this deployment to re-create the endpoint.          | On a development machine, run `stripe listen --print-secret`, pass the secret it prints (whsec\_…) as `webhookSecret`, then forward events with `stripe listen --forward-to` the connection's webhook URL. |
| [`processor_not_available`](https://ownrecord.io/docs/reference/errors.md#processor_not_available)                     | 400         | A fake connection where development access is off.                     | Connect Stripe or Mollie. The fake processor exists only in development and test.                                                                                                                          |
| [`processor_request_failed`](https://ownrecord.io/docs/reference/errors.md#processor_request_failed)                   | 502         | The processor couldn't be reached or refused the request.              | If `retryable` is true, retry with the same Idempotency-Key after a short wait; otherwise check `details.code`.                                                                                            |
| [`upstream_timeout`](https://ownrecord.io/docs/reference/errors.md#upstream_timeout)                                   | 502         | The processor didn't answer in time.                                   | Retry the request with the same Idempotency-Key after a short wait.                                                                                                                                        |
| [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict)                                   | 409         | The connection changed since `expectedVersion`.                        | Fetch the object again and retry the change.                                                                                                                                                               |
| [`scope_required`](https://ownrecord.io/docs/reference/errors.md#scope_required)                                       | 400         | The request names no legal entity.                                     | Call the operation with credentials or a target at its level: an organization, a legal entity, a brand or the platform (`details.required_scope`).                                                         |
| [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied)                                 | 403         | You can't manage processor connections here.                           | Ask an organization owner or admin for a role or scope that allows this action.                                                                                                                            |
| [`fresh_auth_required`](https://ownrecord.io/docs/reference/errors.md#fresh_auth_required)                             | 403         | Your last sign-in is older than 15 minutes.                            | Sign in again, then repeat the action within 15 minutes.                                                                                                                                                   |

It can also return these [common errors](https://ownrecord.io/docs/reference.md#common-errors): [`api_version_unsupported`](https://ownrecord.io/docs/reference/errors.md#api_version_unsupported), [`invalid_json`](https://ownrecord.io/docs/reference/errors.md#invalid_json), [`unsupported_media_type`](https://ownrecord.io/docs/reference/errors.md#unsupported_media_type), [`authentication_required`](https://ownrecord.io/docs/reference/errors.md#authentication_required), [`api_key_invalid`](https://ownrecord.io/docs/reference/errors.md#api_key_invalid), [`idempotency_key_reused`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_reused), [`idempotency_in_progress`](https://ownrecord.io/docs/reference/errors.md#idempotency_in_progress), [`dry_run_not_supported`](https://ownrecord.io/docs/reference/errors.md#dry_run_not_supported), [`approval_required`](https://ownrecord.io/docs/reference/errors.md#approval_required), [`approval_rejected`](https://ownrecord.io/docs/reference/errors.md#approval_rejected), [`approval_expired`](https://ownrecord.io/docs/reference/errors.md#approval_expired), [`stale_approval`](https://ownrecord.io/docs/reference/errors.md#stale_approval), [`approval_policy_denied`](https://ownrecord.io/docs/reference/errors.md#approval_policy_denied), [`rate_limited`](https://ownrecord.io/docs/reference/errors.md#rate_limited), [`internal_error`](https://ownrecord.io/docs/reference/errors.md#internal_error).
