# Test a processor connection

> The processor_connections.test operation: POST /api/v1/processor_connections/{id}/test in the REST API and the processor_connections_test tool in the MCP server.

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.

## At a glance

- **REST:** `POST /api/v1/processor_connections/{id}/test`
- **MCP tool:** `processor_connections_test`
- **Operation ID:** `processor_connections.test`
- **Scope:** Legal entity
- **Permission:** `processors:write`
- **Risk tier:** `write`
- **Dry run:** Supported (send `Ownrecord-Dry-Run: true`)
- **Idempotency:** `Idempotency-Key` required
- **Modes:** Test and live
- **Authentication:** API key with the `processors:write` scope
- **Since:** M4

## Request

### Path parameters

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

### Body

This operation takes no body.

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

### A key that lost a permission

```http
POST /api/v1/processor_connections/pcon_01m4vyt4mxe0s9zzk90373z6p6/test
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
```

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

## 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.                                               |
| [`processor_connection_disconnected`](https://ownrecord.io/docs/reference/errors.md#processor_connection_disconnected) | 409         | The connection is disconnected (its key is gone).             | Connect the processor again, or use another connection of the entity.                                                                              |
| [`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.                            | 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 while it was being tested; test again. | 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 test processor connections here.                    | Ask an organization owner or admin for a role or scope that allows this action.                                                                    |

It can also return these [common errors](https://ownrecord.io/docs/reference.md#common-errors): [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed), [`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_required`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_required), [`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), [`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).
