> ## Documentation Index
> Fetch the complete documentation index at: https://docs.venlyfinance.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API credentials

> Create, rotate and disable your company's OAuth client credentials yourself — and why the secret is shown exactly once.

Your Finance API access is an OAuth `client_credentials` pair. Until 1.8.0 Venly provisioned it for
you and a compromised secret meant a support ticket. You can now manage it through the API.

## What a credential is

| | |
| - | - |
| `clientId` | Server-generated from your company and the `name` you give — never chosen by you. |
| `secret` | Shown **once**, on create and on rotate. Finance does not store it and no endpoint returns it later. |
| `status` | `ACTIVE` or `DISABLED`. There is no deleted state. |
| Roles | Your company's API access — the customer role plus the transfer role matching your wallet type. |

<Warning>
  **Each company holds one credential.** Disabling it does not free the slot: the cap counts disabled
  credentials too, because a credential is never deleted. If you believe a secret is compromised, rotate
  it — don't try to create a second one.
</Warning>

## Who can call these endpoints

The four credential endpoints require the `manage:credentials` scope, and **no API credential ever
holds it** — it is a dashboard-user capability. That is deliberate: it means no credential can create,
rotate or disable another, so a leaked API secret cannot be used to mint a replacement for itself.

Plan for the consequence: if the only dashboard user holding `manage:credentials` is disabled or
leaves, restoring access to this surface requires Venly.

## Create

```bash theme={null}
curl -X POST https://api-staging.venlyfinance.com/v1/api-credentials \
  -H "Authorization: Bearer {dashboard_user_token}" \
  -H "Content-Type: application/json" \
  -d '{ "name": "checkout-service" }'
```

```json Response (201) theme={null}
{
  "success": true,
  "result": {
    "clientId": "fin-8f2c1a9b-0000-4000-8000-000000000001-checkout-service",
    "name": "checkout-service",
    "secret": "generated-value-shown-exactly-once",
    "status": "ACTIVE",
    "createdAt": "2026-09-03T10:12:00Z"
  }
}
```

Store `secret` in your secrets manager **from this response**. The
[list endpoint](/api-reference/Finance-API/api-credentials/list-api-credentials) has no `secret` field.

## Rotate

```bash theme={null}
curl -X POST https://api-staging.venlyfinance.com/v1/api-credentials/{clientId}/secret \
  -H "Authorization: Bearer {dashboard_user_token}"
```

The old secret stops working **immediately**; tokens already issued against it stay valid until they
expire (about five minutes). So the safe sequence is:

<Steps>
  <Step title="Be ready to deploy">Have the place the new secret goes — your secrets manager, your config — ready to accept it.</Step>
  <Step title="Rotate">Call the endpoint and capture `result.secret`.</Step>
  <Step title="Deploy the new secret">Roll it out. Any service still holding the old secret fails its **next** token request, not its in-flight calls.</Step>
</Steps>

Rotation is refused on a `DISABLED` credential (`409 credential-disabled`) — enable it first.

## Disable and re-enable

```bash theme={null}
curl -X PUT https://api-staging.venlyfinance.com/v1/api-credentials/{clientId}/status \
  -H "Authorization: Bearer {dashboard_user_token}" \
  -H "Content-Type: application/json" \
  -d '{ "status": "DISABLED" }'
```

Disabling is a pause: the credential keeps its `clientId`, secret, roles and history, and simply stops
issuing tokens. Setting `ACTIVE` again restores it unchanged.

<Note>
  **If Venly provisioned more than one credential for your company** before this endpoint existed, the
  one-credential cap applies to re-enabling: while one is active, enabling another is refused with
  `409 credential-limit-exceeded`, and there is no self-service way past it. For such a company,
  disabling is how you converge on a single credential — do it deliberately.
</Note>

## Errors

| HTTP | `code` | Meaning |
| - | - | - |
| `403` | `forbidden` | The token lacks `manage:credentials`. An API credential's own token always gets this. |
| `404` | `credential-not-found` | Unknown `clientId` — or one belonging to another company. The two are indistinguishable by design. |
| `409` | `credential-limit-exceeded` | Creating a second credential, or enabling one while another is active. |
| `409` | `credential-name-unavailable` | No free `clientId` could be derived from this `name`; choose another. |
| `409` | `credential-disabled` | Rotating a disabled credential. |
| `503` | `keycloak-unavailable` | The identity provider was unreachable. Retry with backoff. |

## Next steps

<CardGroup cols={2}>
  <Card title="Authentication" icon="lock" href="/getting-started/authentication">
    Turning the credential into a bearer token, and the scopes each endpoint needs.
  </Card>

  <Card title="Create API credentials" icon="plus" href="/api-reference/Finance-API/api-credentials/create-api-credentials">
    The endpoint reference.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.